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, 2013, 2014, 2015 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
96 #define DATADIR "~~"
\r
98 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
\r
100 int myrandom(void);
\r
101 void mysrandom(unsigned int seed);
\r
103 extern int whiteFlag, blackFlag;
\r
104 Boolean flipClock = FALSE;
\r
105 extern HANDLE chatHandle[];
\r
106 extern enum ICS_TYPE ics_type;
\r
108 int MySearchPath P((char *installDir, char *name, char *fullname));
\r
109 int MyGetFullPathName P((char *name, char *fullname));
\r
110 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
111 VOID NewVariantPopup(HWND hwnd);
\r
112 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
113 /*char*/int promoChar));
\r
114 void DisplayMove P((int moveNumber));
\r
115 void ChatPopUp P((char *s));
\r
117 ChessSquare piece;
\r
118 POINT pos; /* window coordinates of current pos */
\r
119 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
120 POINT from; /* board coordinates of the piece's orig pos */
\r
121 POINT to; /* board coordinates of the piece's new pos */
\r
124 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
127 POINT start; /* window coordinates of start pos */
\r
128 POINT pos; /* window coordinates of current pos */
\r
129 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
130 POINT from; /* board coordinates of the piece's orig pos */
\r
134 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };
\r
137 POINT sq[2]; /* board coordinates of from, to squares */
\r
140 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
141 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
142 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
143 static HighlightInfo oldPartnerHighlight = { {{-1, -1}, {-1, -1}} };
\r
145 typedef struct { // [HGM] atomic
\r
146 int fromX, fromY, toX, toY, radius;
\r
149 static ExplodeInfo explodeInfo;
\r
151 /* Window class names */
\r
152 char szAppName[] = "WinBoard";
\r
153 char szConsoleName[] = "WBConsole";
\r
155 /* Title bar text */
\r
156 char szTitle[] = "WinBoard";
\r
157 char szConsoleTitle[] = "I C S Interaction";
\r
160 char *settingsFileName;
\r
161 Boolean saveSettingsOnExit;
\r
162 char installDir[MSG_SIZ];
\r
163 int errorExitStatus;
\r
165 BoardSize boardSize;
\r
166 Boolean chessProgram;
\r
167 //static int boardX, boardY;
\r
168 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
169 int squareSize, lineGap, minorSize;
\r
170 static int winW, winH;
\r
171 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
172 static int logoHeight = 0;
\r
173 static char messageText[MESSAGE_TEXT_MAX];
\r
174 static int clockTimerEvent = 0;
\r
175 static int loadGameTimerEvent = 0;
\r
176 static int analysisTimerEvent = 0;
\r
177 static DelayedEventCallback delayedTimerCallback;
\r
178 static int delayedTimerEvent = 0;
\r
179 static int buttonCount = 2;
\r
180 char *icsTextMenuString;
\r
182 char *firstChessProgramNames;
\r
183 char *secondChessProgramNames;
\r
185 #define PALETTESIZE 256
\r
187 HINSTANCE hInst; /* current instance */
\r
188 Boolean alwaysOnTop = FALSE;
\r
190 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
191 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
192 COLORREF markerColor[8] = { 0x00FFFF, 0x0000FF, 0x00FF00, 0xFF0000, 0xFFFF00, 0xFF00FF, 0xFFFFFF, 0x000000 };
\r
194 ColorClass currentColorClass;
\r
196 static HWND savedHwnd;
\r
197 HWND hCommPort = NULL; /* currently open comm port */
\r
198 static HWND hwndPause; /* pause button */
\r
199 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
200 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
201 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
202 explodeBrush, /* [HGM] atomic */
\r
203 markerBrush[8], /* [HGM] markers */
\r
204 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
205 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
206 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
207 static HPEN gridPen = NULL;
\r
208 static HPEN highlightPen = NULL;
\r
209 static HPEN premovePen = NULL;
\r
210 static NPLOGPALETTE pLogPal;
\r
211 static BOOL paletteChanged = FALSE;
\r
212 static HICON iconWhite, iconBlack, iconCurrent;
\r
213 static int doingSizing = FALSE;
\r
214 static int lastSizing = 0;
\r
215 static int prevStderrPort;
\r
216 static HBITMAP userLogo;
\r
218 static HBITMAP liteBackTexture = NULL;
\r
219 static HBITMAP darkBackTexture = NULL;
\r
220 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
221 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
222 static int backTextureSquareSize = 0;
\r
223 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
225 #if __GNUC__ && !defined(_winmajor)
\r
226 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
229 #if defined(_winmajor)
\r
230 #define oldDialog (_winmajor < 4)
\r
232 #define oldDialog 0
\r
236 #define INTERNATIONAL
\r
238 #ifdef INTERNATIONAL
\r
239 # define _(s) T_(s)
\r
245 # define Translate(x, y)
\r
246 # define LoadLanguageFile(s)
\r
249 #ifdef INTERNATIONAL
\r
251 Boolean barbaric; // flag indicating if translation is needed
\r
253 // list of item numbers used in each dialog (used to alter language at run time)
\r
255 #define ABOUTBOX -1 /* not sure why these are needed */
\r
256 #define ABOUTBOX2 -1
\r
258 int dialogItems[][42] = {
\r
259 { ABOUTBOX, IDOK, OPT_MESS, 400 },
\r
260 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed,
\r
261 OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors, IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL },
\r
262 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,
\r
263 OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds,
\r
264 OPT_Ranget, IDOK, IDCANCEL },
\r
265 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
266 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
267 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
268 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
269 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
270 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
271 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
272 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
273 { ABOUTBOX2, IDC_ChessBoard },
\r
274 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
275 OPT_GameListClose, IDC_GameListDoFilter },
\r
276 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
277 { DLG_Error, IDOK },
\r
278 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
279 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
280 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
281 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
282 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
283 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
284 { DLG_IndexNumber, IDC_Index },
\r
285 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
286 { DLG_TypeInName, IDOK, IDCANCEL },
\r
287 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
288 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
289 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
290 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
291 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
292 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
293 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
294 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
295 OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove },
\r
296 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
297 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
298 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
299 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
300 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
301 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
302 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
303 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
304 GPB_General, GPB_Alarm, OPT_AutoCreate },
\r
305 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
306 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
307 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
308 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
309 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
310 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
311 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
312 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid },
\r
313 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
314 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
315 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
316 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
317 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
318 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
319 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,
\r
320 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
321 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
322 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
323 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
324 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,
\r
325 OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7,
\r
326 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
327 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
328 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
329 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
330 { DLG_MoveHistory },
\r
331 { DLG_EvalGraph },
\r
332 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
333 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
334 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
335 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
336 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
337 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
338 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
339 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
340 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
344 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];
\r
345 static int lastChecked;
\r
346 static char oldLanguage[MSG_SIZ], *menuText[10][30];
\r
347 extern int tinyLayout;
\r
348 extern char * menuBarText[][10];
\r
351 LoadLanguageFile(char *name)
\r
352 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
354 int i=0, j=0, n=0, k;
\r
357 if(!name || name[0] == NULLCHAR) return;
\r
358 snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension
\r
359 appData.language = oldLanguage;
\r
360 if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on
\r
361 if((f = fopen(buf, "r")) == NULL) return;
\r
362 while((k = fgetc(f)) != EOF) {
\r
363 if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }
\r
364 languageBuf[i] = k;
\r
366 if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {
\r
368 if(p = strstr(languageBuf + n + 1, "\" === \"")) {
\r
369 if(p > languageBuf+n+2 && p+8 < languageBuf+i) {
\r
370 if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }
\r
371 english[j] = languageBuf + n + 1; *p = 0;
\r
372 foreign[j++] = p + 7; languageBuf[i-1] = 0;
\r
373 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);
\r
378 } else if(i > 0 && languageBuf[i-1] == '\\') {
\r
380 case 'n': k = '\n'; break;
\r
381 case 'r': k = '\r'; break;
\r
382 case 't': k = '\t'; break;
\r
384 languageBuf[--i] = k;
\r
389 barbaric = (j != 0);
\r
390 safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );
\r
395 { // return the translation of the given string
\r
396 // efficiency can be improved a lot...
\r
398 static char buf[MSG_SIZ];
\r
399 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
400 if(!barbaric) return s;
\r
401 if(!s) return ""; // sanity
\r
402 while(english[i]) {
\r
403 if(!strcmp(s, english[i])) return foreign[i];
\r
404 if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending
\r
405 snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion
\r
414 Translate(HWND hDlg, int dialogID)
\r
415 { // translate all text items in the given dialog
\r
417 char buf[MSG_SIZ], *s;
\r
418 if(!barbaric) return;
\r
419 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
420 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
421 GetWindowText( hDlg, buf, MSG_SIZ );
\r
423 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
424 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
425 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
426 if(strlen(buf) == 0) continue;
\r
428 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
433 TranslateOneMenu(int i, HMENU subMenu)
\r
436 static MENUITEMINFO info;
\r
438 info.cbSize = sizeof(MENUITEMINFO);
\r
439 info.fMask = MIIM_STATE | MIIM_TYPE;
\r
440 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
442 info.dwTypeData = buf;
\r
443 info.cch = sizeof(buf);
\r
444 GetMenuItemInfo(subMenu, j, TRUE, &info);
\r
446 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );
\r
447 else menuText[i][j] = strdup(buf); // remember original on first change
\r
449 if(buf[0] == NULLCHAR) continue;
\r
450 info.dwTypeData = T_(buf);
\r
451 info.cch = strlen(buf)+1;
\r
452 SetMenuItemInfo(subMenu, j, TRUE, &info);
\r
458 TranslateMenus(int addLanguage)
\r
461 WIN32_FIND_DATA fileData;
\r
463 #define IDM_English 1970
\r
465 HMENU mainMenu = GetMenu(hwndMain);
\r
466 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
467 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
468 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
469 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
470 TranslateOneMenu(i, subMenu);
\r
472 DrawMenuBar(hwndMain);
\r
475 if(!addLanguage) return;
\r
476 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
477 HMENU mainMenu = GetMenu(hwndMain);
\r
478 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
479 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
480 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
481 i = 0; lastChecked = IDM_English;
\r
483 char *p, *q = fileData.cFileName;
\r
484 int checkFlag = MF_UNCHECKED;
\r
485 languageFile[i] = strdup(q);
\r
486 if(barbaric && !strcmp(oldLanguage, q)) {
\r
487 checkFlag = MF_CHECKED;
\r
488 lastChecked = IDM_English + i + 1;
\r
489 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
491 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
492 p = strstr(fileData.cFileName, ".lng");
\r
494 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
495 } while(FindNextFile(hFind, &fileData));
\r
502 #define IDM_RecentEngines 3000
\r
505 RecentEngineMenu (char *s)
\r
507 if(appData.icsActive) return;
\r
508 if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty
\r
509 HMENU mainMenu = GetMenu(hwndMain);
\r
510 HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu
\r
511 int i=IDM_RecentEngines;
\r
512 recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu
\r
513 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
515 char *p = strchr(s, '\n');
\r
516 if(p == NULL) return; // malformed!
\r
518 AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);
\r
532 int cliWidth, cliHeight;
\r
535 SizeInfo sizeInfo[] =
\r
537 { "tiny", 21, 0, 1, 2, 0, 0 },
\r
538 { "teeny", 25, 1, 1, 2, 0, 0 },
\r
539 { "dinky", 29, 1, 1, 2, 0, 0 },
\r
540 { "petite", 33, 1, 1, 2, 0, 0 },
\r
541 { "slim", 37, 2, 1, 1, 0, 0 },
\r
542 { "small", 40, 2, 1, 1, 0, 0 },
\r
543 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
544 { "middling", 49, 2, 0, 0, 0, 0 },
\r
545 { "average", 54, 2, 0, 0, 0, 0 },
\r
546 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
547 { "medium", 64, 3, 0, 0, 0, 0 },
\r
548 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
549 { "large", 80, 3, 0, 0, 0, 0 },
\r
550 { "big", 87, 3, 0, 0, 0, 0 },
\r
551 { "huge", 95, 3, 0, 0, 0, 0 },
\r
552 { "giant", 108, 3, 0, 0, 0, 0 },
\r
553 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
554 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
555 { NULL, 0, 0, 0, 0, 0, 0 }
\r
558 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
559 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
561 { 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
562 { 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
563 { 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
564 { 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
565 { 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
566 { 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
567 { 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
568 { 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
569 { 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
570 { 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
571 { 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
572 { 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
573 { 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
574 { 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
575 { 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
576 { 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
577 { 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
578 { 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
581 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
590 #define BUTTON_WIDTH (tinyLayout == 2 ? 16 : 32)
\r
591 #define N_BUTTONS 5
\r
593 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
595 {"<<", IDM_ToStart, NULL, NULL},
\r
596 {"<", IDM_Backward, NULL, NULL},
\r
597 {"P", IDM_Pause, NULL, NULL},
\r
598 {">", IDM_Forward, NULL, NULL},
\r
599 {">>", IDM_ToEnd, NULL, NULL},
\r
602 int tinyLayout = 0, smallLayout = 0;
\r
603 #define MENU_BAR_ITEMS 9
\r
604 char *menuBarText[3][MENU_BAR_ITEMS+1] = {
\r
605 { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },
\r
606 { N_("&Fil"), N_("&Ed"), N_("&Vw"), N_("&Mod"), N_("&Act"), N_("E&ng"), N_("&Opt"), N_("&Hlp"), NULL },
\r
607 { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },
\r
611 MySound sounds[(int)NSoundClasses];
\r
612 MyTextAttribs textAttribs[(int)NColorClasses];
\r
614 MyColorizeAttribs colorizeAttribs[] = {
\r
615 { (COLORREF)0, 0, N_("Shout Text") },
\r
616 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
617 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
618 { (COLORREF)0, 0, N_("Channel Text") },
\r
619 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
620 { (COLORREF)0, 0, N_("Tell Text") },
\r
621 { (COLORREF)0, 0, N_("Challenge Text") },
\r
622 { (COLORREF)0, 0, N_("Request Text") },
\r
623 { (COLORREF)0, 0, N_("Seek Text") },
\r
624 { (COLORREF)0, 0, N_("Normal Text") },
\r
625 { (COLORREF)0, 0, N_("None") }
\r
630 static char *commentTitle;
\r
631 static char *commentText;
\r
632 static int commentIndex;
\r
633 static Boolean editComment = FALSE;
\r
636 char errorTitle[MSG_SIZ];
\r
637 char errorMessage[2*MSG_SIZ];
\r
638 HWND errorDialog = NULL;
\r
639 BOOLEAN moveErrorMessageUp = FALSE;
\r
640 BOOLEAN consoleEcho = TRUE;
\r
641 CHARFORMAT consoleCF;
\r
642 COLORREF consoleBackgroundColor;
\r
644 char *programVersion;
\r
650 typedef int CPKind;
\r
659 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
662 #define INPUT_SOURCE_BUF_SIZE 4096
\r
664 typedef struct _InputSource {
\r
671 char buf[INPUT_SOURCE_BUF_SIZE];
\r
675 InputCallback func;
\r
676 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
680 InputSource *consoleInputSource;
\r
685 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
686 VOID ConsoleCreate();
\r
688 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
689 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
690 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
691 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
693 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
694 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
695 void ParseIcsTextMenu(char *icsTextMenuString);
\r
696 VOID PopUpNameDialog(char firstchar);
\r
697 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
701 int GameListOptions();
\r
703 int dummy; // [HGM] for obsolete args
\r
705 HWND hwndMain = NULL; /* root window*/
\r
706 HWND hwndConsole = NULL;
\r
707 HWND commentDialog = NULL;
\r
708 HWND moveHistoryDialog = NULL;
\r
709 HWND evalGraphDialog = NULL;
\r
710 HWND engineOutputDialog = NULL;
\r
711 HWND gameListDialog = NULL;
\r
712 HWND editTagsDialog = NULL;
\r
714 int commentUp = FALSE;
\r
716 WindowPlacement wpMain;
\r
717 WindowPlacement wpConsole;
\r
718 WindowPlacement wpComment;
\r
719 WindowPlacement wpMoveHistory;
\r
720 WindowPlacement wpEvalGraph;
\r
721 WindowPlacement wpEngineOutput;
\r
722 WindowPlacement wpGameList;
\r
723 WindowPlacement wpTags;
\r
725 VOID EngineOptionsPopup(); // [HGM] settings
\r
727 VOID GothicPopUp(char *title, VariantClass variant);
\r
729 * Setting "frozen" should disable all user input other than deleting
\r
730 * the window. We do this while engines are initializing themselves.
\r
732 static int frozen = 0;
\r
733 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
739 if (frozen) return;
\r
741 hmenu = GetMenu(hwndMain);
\r
742 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
743 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
745 DrawMenuBar(hwndMain);
\r
748 /* Undo a FreezeUI */
\r
754 if (!frozen) return;
\r
756 hmenu = GetMenu(hwndMain);
\r
757 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
758 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
760 DrawMenuBar(hwndMain);
\r
763 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
765 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
771 #define JAWS_ALT_INTERCEPT
\r
772 #define JAWS_KBUP_NAVIGATION
\r
773 #define JAWS_KBDOWN_NAVIGATION
\r
774 #define JAWS_MENU_ITEMS
\r
775 #define JAWS_SILENCE
\r
776 #define JAWS_REPLAY
\r
778 #define JAWS_COPYRIGHT
\r
779 #define JAWS_DELETE(X) X
\r
780 #define SAYMACHINEMOVE()
\r
784 /*---------------------------------------------------------------------------*\
\r
788 \*---------------------------------------------------------------------------*/
\r
790 static void HandleMessage P((MSG *message));
\r
791 static HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
794 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
795 LPSTR lpCmdLine, int nCmdShow)
\r
798 // INITCOMMONCONTROLSEX ex;
\r
802 LoadLibrary("RICHED32.DLL");
\r
803 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
805 if (!InitApplication(hInstance)) {
\r
808 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
815 // InitCommonControlsEx(&ex);
\r
816 InitCommonControls();
\r
818 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
819 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
820 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
822 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
824 while (GetMessage(&msg, /* message structure */
\r
825 NULL, /* handle of window receiving the message */
\r
826 0, /* lowest message to examine */
\r
827 0)) /* highest message to examine */
\r
829 HandleMessage(&msg);
\r
833 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
837 HandleMessage (MSG *message)
\r
839 MSG msg = *message;
\r
841 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
842 // [HGM] navigate: switch between all windows with tab
\r
843 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
844 int i, currentElement = 0;
\r
846 // first determine what element of the chain we come from (if any)
\r
847 if(appData.icsActive) {
\r
848 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
849 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
851 if(engineOutputDialog && EngineOutputIsUp()) {
\r
852 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
853 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
855 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
856 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
858 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
859 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
860 if(msg.hwnd == e1) currentElement = 2; else
\r
861 if(msg.hwnd == e2) currentElement = 3; else
\r
862 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
863 if(msg.hwnd == mh) currentElement = 4; else
\r
864 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
865 if(msg.hwnd == hText) currentElement = 5; else
\r
866 if(msg.hwnd == hInput) currentElement = 6; else
\r
867 for (i = 0; i < N_BUTTONS; i++) {
\r
868 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
871 // determine where to go to
\r
872 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
874 currentElement = (currentElement + direction) % 7;
\r
875 switch(currentElement) {
\r
877 h = hwndMain; break; // passing this case always makes the loop exit
\r
879 h = buttonDesc[0].hwnd; break; // could be NULL
\r
881 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
884 if(!EngineOutputIsUp()) continue;
\r
887 if(!MoveHistoryIsUp()) continue;
\r
889 // case 6: // input to eval graph does not seem to get here!
\r
890 // if(!EvalGraphIsUp()) continue;
\r
891 // h = evalGraphDialog; break;
\r
893 if(!appData.icsActive) continue;
\r
897 if(!appData.icsActive) continue;
\r
903 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
904 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
907 return; // this message now has been processed
\r
911 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
912 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
913 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
914 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
915 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
916 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
917 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
918 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
919 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
920 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
921 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
922 for(i=0; i<MAX_CHAT; i++)
\r
923 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
926 if(done) return; // [HGM] chat: end patch
\r
927 TranslateMessage(&msg); /* Translates virtual key codes */
\r
928 DispatchMessage(&msg); /* Dispatches message to window */
\r
934 { /* Dispatch pending messages */
\r
936 while (PeekMessage(&msg, /* message structure */
\r
937 NULL, /* handle of window receiving the message */
\r
938 0, /* lowest message to examine */
\r
939 0, /* highest message to examine */
\r
942 HandleMessage(&msg);
\r
946 /*---------------------------------------------------------------------------*\
\r
948 * Initialization functions
\r
950 \*---------------------------------------------------------------------------*/
\r
954 { // update user logo if necessary
\r
955 static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;
\r
957 if(appData.autoLogo) {
\r
958 curName = UserName();
\r
959 if(strcmp(curName, oldUserName)) {
\r
960 GetCurrentDirectory(MSG_SIZ, dir);
\r
961 SetCurrentDirectory(installDir);
\r
962 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
963 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
964 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
965 if(userLogo == NULL)
\r
966 userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
967 SetCurrentDirectory(dir); /* return to prev directory */
\r
973 InitApplication(HINSTANCE hInstance)
\r
977 /* Fill in window class structure with parameters that describe the */
\r
980 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
981 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
982 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
983 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
984 wc.hInstance = hInstance; /* Owner of this class */
\r
985 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
986 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
987 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
988 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
989 wc.lpszClassName = szAppName; /* Name to register as */
\r
991 /* Register the window class and return success/failure code. */
\r
992 if (!RegisterClass(&wc)) return FALSE;
\r
994 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
995 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
997 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
998 wc.hInstance = hInstance;
\r
999 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
1000 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
1001 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
1002 wc.lpszMenuName = NULL;
\r
1003 wc.lpszClassName = szConsoleName;
\r
1005 if (!RegisterClass(&wc)) return FALSE;
\r
1010 /* Set by InitInstance, used by EnsureOnScreen */
\r
1011 int screenHeight, screenWidth;
\r
1012 RECT screenGeometry;
\r
1015 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
1017 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
1018 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
1019 if (*x > screenGeometry.right - 32) *x = screenGeometry.left;
\r
1020 if (*y > screenGeometry.bottom - 32) *y = screenGeometry.top;
\r
1021 if (*x < screenGeometry.left + minX) *x = screenGeometry.left + minX;
\r
1022 if (*y < screenGeometry.top + minY) *y = screenGeometry.top + minY;
\r
1026 LoadLogo(ChessProgramState *cps, int n, Boolean ics)
\r
1028 char buf[MSG_SIZ], dir[MSG_SIZ];
\r
1029 GetCurrentDirectory(MSG_SIZ, dir);
\r
1030 SetCurrentDirectory(installDir);
\r
1031 if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {
\r
1032 cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1034 if (cps->programLogo == NULL && appData.debugMode) {
\r
1035 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );
\r
1037 } else if(appData.autoLogo) {
\r
1038 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
\r
1039 char *opponent = "";
\r
1040 if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;
\r
1041 if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;
\r
1042 sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);
\r
1043 if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {
\r
1044 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
1045 cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1048 if(appData.directory[n] && appData.directory[n][0]) {
\r
1049 SetCurrentDirectory(appData.directory[n]);
\r
1050 cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1053 SetCurrentDirectory(dir); /* return to prev directory */
\r
1059 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
1060 backTextureSquareSize = 0; // kludge to force recalculation of texturemode
\r
1062 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1063 if(liteBackTexture) DeleteObject(liteBackTexture);
\r
1064 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1065 liteBackTextureMode = appData.liteBackTextureMode;
\r
1067 if (liteBackTexture == NULL && appData.debugMode) {
\r
1068 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1072 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1073 if(darkBackTexture) DeleteObject(darkBackTexture);
\r
1074 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1075 darkBackTextureMode = appData.darkBackTextureMode;
\r
1077 if (darkBackTexture == NULL && appData.debugMode) {
\r
1078 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1083 #ifndef SM_CXVIRTUALSCREEN
\r
1084 #define SM_CXVIRTUALSCREEN 78
\r
1086 #ifndef SM_CYVIRTUALSCREEN
\r
1087 #define SM_CYVIRTUALSCREEN 79
\r
1089 #ifndef SM_XVIRTUALSCREEN
\r
1090 #define SM_XVIRTUALSCREEN 76
\r
1092 #ifndef SM_YVIRTUALSCREEN
\r
1093 #define SM_YVIRTUALSCREEN 77
\r
1099 screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
\r
1100 if( !screenHeight ) screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1101 screenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
\r
1102 if( !screenWidth ) screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1103 screenGeometry.left = GetSystemMetrics(SM_XVIRTUALSCREEN);
\r
1104 screenGeometry.top = GetSystemMetrics(SM_YVIRTUALSCREEN);
\r
1105 screenGeometry.right = screenGeometry.left + screenWidth;
\r
1106 screenGeometry.bottom = screenGeometry.top + screenHeight;
\r
1109 ChessProgramState broadcast;
\r
1112 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
1114 HWND hwnd; /* Main window handle. */
\r
1116 WINDOWPLACEMENT wp;
\r
1119 hInst = hInstance; /* Store instance handle in our global variable */
\r
1120 programName = szAppName;
\r
1122 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
1123 *filepart = NULLCHAR;
\r
1124 SetCurrentDirectory(installDir);
\r
1126 GetCurrentDirectory(MSG_SIZ, installDir);
\r
1128 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
1130 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
1131 /* xboard, and older WinBoards, controlled the move sound with the
\r
1132 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
1133 always turn the option on (so that the backend will call us),
\r
1134 then let the user turn the sound off by setting it to silence if
\r
1135 desired. To accommodate old winboard.ini files saved by old
\r
1136 versions of WinBoard, we also turn off the sound if the option
\r
1137 was initially set to false. [HGM] taken out of InitAppData */
\r
1138 if (!appData.ringBellAfterMoves) {
\r
1139 sounds[(int)SoundMove].name = strdup("");
\r
1140 appData.ringBellAfterMoves = TRUE;
\r
1142 if (appData.debugMode) {
\r
1143 char *c = appData.nameOfDebugFile;
\r
1144 if(strstr(c, "///") == c) {
\r
1145 broadcast.which = "broadcaster";
\r
1146 broadcast.pr = NoProc;
\r
1147 broadcast.isr = NULL;
\r
1148 broadcast.program = c + 3;
\r
1149 broadcast.dir = ".";
\r
1150 broadcast.host = "localhost";
\r
1151 StartChessProgram(&broadcast);
\r
1152 debugFP = (FILE*) _fdopen(_open_osfhandle((long)(((ChildProc*)(broadcast.pr))->hTo), _O_WRONLY), "w");
\r
1154 debugFP = fopen(c, "w");
\r
1155 setbuf(debugFP, NULL);
\r
1158 LoadLanguageFile(appData.language);
\r
1162 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
1163 // InitEngineUCI( installDir, &second );
\r
1165 /* Create a main window for this application instance. */
\r
1166 hwnd = CreateWindow(szAppName, szTitle,
\r
1167 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
1168 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
1169 NULL, NULL, hInstance, NULL);
\r
1172 /* If window could not be created, return "failure" */
\r
1177 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1178 LoadLogo(&first, 0, FALSE);
\r
1179 LoadLogo(&second, 1, appData.icsActive);
\r
1183 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1184 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1185 iconCurrent = iconWhite;
\r
1186 InitDrawingColors();
\r
1188 InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args
\r
1189 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1190 /* Compute window size for each board size, and use the largest
\r
1191 size that fits on this screen as the default. */
\r
1192 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1193 if (boardSize == (BoardSize)-1 &&
\r
1194 winH <= screenHeight
\r
1195 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1196 && winW <= screenWidth) {
\r
1197 boardSize = (BoardSize)ibs;
\r
1201 InitDrawingSizes(boardSize, 0);
\r
1202 RecentEngineMenu(appData.recentEngineList);
\r
1204 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1206 /* [AS] Load textures if specified */
\r
1209 mysrandom( (unsigned) time(NULL) );
\r
1211 /* [AS] Restore layout */
\r
1212 if( wpMoveHistory.visible ) {
\r
1213 MoveHistoryPopUp();
\r
1216 if( wpEvalGraph.visible ) {
\r
1220 if( wpEngineOutput.visible ) {
\r
1221 EngineOutputPopUp();
\r
1224 /* Make the window visible; update its client area; and return "success" */
\r
1225 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1226 wp.length = sizeof(WINDOWPLACEMENT);
\r
1228 wp.showCmd = nCmdShow;
\r
1229 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1230 wp.rcNormalPosition.left = wpMain.x;
\r
1231 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1232 wp.rcNormalPosition.top = wpMain.y;
\r
1233 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1234 SetWindowPlacement(hwndMain, &wp);
\r
1236 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1238 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1239 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1241 if (hwndConsole) {
\r
1243 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1244 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1246 ShowWindow(hwndConsole, nCmdShow);
\r
1247 SetActiveWindow(hwndConsole);
\r
1249 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1250 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1259 HMENU hmenu = GetMenu(hwndMain);
\r
1261 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1262 MF_BYCOMMAND|((appData.icsActive &&
\r
1263 *appData.icsCommPort != NULLCHAR) ?
\r
1264 MF_ENABLED : MF_GRAYED));
\r
1265 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1266 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1267 MF_CHECKED : MF_UNCHECKED));
\r
1268 EnableMenuItem(hmenu, IDM_SaveSelected, MF_GRAYED);
\r
1271 //---------------------------------------------------------------------------------------------------------
\r
1273 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1274 #define XBOARD FALSE
\r
1276 #define OPTCHAR "/"
\r
1277 #define SEPCHAR "="
\r
1278 #define TOPLEVEL 0
\r
1282 // front-end part of option handling
\r
1285 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1287 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1288 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1291 lf->lfEscapement = 0;
\r
1292 lf->lfOrientation = 0;
\r
1293 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1294 lf->lfItalic = mfp->italic;
\r
1295 lf->lfUnderline = mfp->underline;
\r
1296 lf->lfStrikeOut = mfp->strikeout;
\r
1297 lf->lfCharSet = mfp->charset;
\r
1298 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1302 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1303 lf->lfQuality = DEFAULT_QUALITY;
\r
1304 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1305 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1309 CreateFontInMF(MyFont *mf)
\r
1311 LFfromMFP(&mf->lf, &mf->mfp);
\r
1312 if (mf->hf) DeleteObject(mf->hf);
\r
1313 mf->hf = CreateFontIndirect(&mf->lf);
\r
1316 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1318 colorVariable[] = {
\r
1319 &whitePieceColor,
\r
1320 &blackPieceColor,
\r
1321 &lightSquareColor,
\r
1322 &darkSquareColor,
\r
1323 &highlightSquareColor,
\r
1324 &premoveHighlightColor,
\r
1326 &consoleBackgroundColor,
\r
1327 &appData.fontForeColorWhite,
\r
1328 &appData.fontBackColorWhite,
\r
1329 &appData.fontForeColorBlack,
\r
1330 &appData.fontBackColorBlack,
\r
1331 &appData.evalHistColorWhite,
\r
1332 &appData.evalHistColorBlack,
\r
1333 &appData.highlightArrowColor,
\r
1336 /* Command line font name parser. NULL name means do nothing.
\r
1337 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1338 For backward compatibility, syntax without the colon is also
\r
1339 accepted, but font names with digits in them won't work in that case.
\r
1342 ParseFontName(char *name, MyFontParams *mfp)
\r
1345 if (name == NULL) return;
\r
1347 q = strchr(p, ':');
\r
1349 if (q - p >= sizeof(mfp->faceName))
\r
1350 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1351 memcpy(mfp->faceName, p, q - p);
\r
1352 mfp->faceName[q - p] = NULLCHAR;
\r
1355 q = mfp->faceName;
\r
1357 while (*p && !isdigit(*p)) {
\r
1359 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1360 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1362 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1365 if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);
\r
1366 mfp->pointSize = (float) atof(p);
\r
1367 mfp->bold = (strchr(p, 'b') != NULL);
\r
1368 mfp->italic = (strchr(p, 'i') != NULL);
\r
1369 mfp->underline = (strchr(p, 'u') != NULL);
\r
1370 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1371 mfp->charset = DEFAULT_CHARSET;
\r
1372 q = strchr(p, 'c');
\r
1374 mfp->charset = (BYTE) atoi(q+1);
\r
1378 ParseFont(char *name, int number)
\r
1379 { // wrapper to shield back-end from 'font'
\r
1380 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1385 { // in WB we have a 2D array of fonts; this initializes their description
\r
1387 /* Point font array elements to structures and
\r
1388 parse default font names */
\r
1389 for (i=0; i<NUM_FONTS; i++) {
\r
1390 for (j=0; j<NUM_SIZES; j++) {
\r
1391 font[j][i] = &fontRec[j][i];
\r
1392 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1399 { // here we create the actual fonts from the selected descriptions
\r
1401 for (i=0; i<NUM_FONTS; i++) {
\r
1402 for (j=0; j<NUM_SIZES; j++) {
\r
1403 CreateFontInMF(font[j][i]);
\r
1407 /* Color name parser.
\r
1408 X version accepts X color names, but this one
\r
1409 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1411 ParseColorName(char *name)
\r
1413 int red, green, blue, count;
\r
1414 char buf[MSG_SIZ];
\r
1416 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1418 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1419 &red, &green, &blue);
\r
1422 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1423 DisplayError(buf, 0);
\r
1424 return RGB(0, 0, 0);
\r
1426 return PALETTERGB(red, green, blue);
\r
1430 ParseColor(int n, char *name)
\r
1431 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1432 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1436 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1438 char *e = argValue;
\r
1442 if (*e == 'b') eff |= CFE_BOLD;
\r
1443 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1444 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1445 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1446 else if (*e == '#' || isdigit(*e)) break;
\r
1450 *color = ParseColorName(e);
\r
1454 ParseTextAttribs(ColorClass cc, char *s)
\r
1455 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1456 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1457 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1461 ParseBoardSize(void *addr, char *name)
\r
1462 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1463 BoardSize bs = SizeTiny;
\r
1464 while (sizeInfo[bs].name != NULL) {
\r
1465 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1466 *(BoardSize *)addr = bs;
\r
1471 ExitArgError(_("Unrecognized board size value"), name, TRUE);
\r
1476 { // [HGM] import name from appData first
\r
1479 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1480 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1481 textAttribs[cc].sound.data = NULL;
\r
1482 MyLoadSound(&textAttribs[cc].sound);
\r
1484 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1485 textAttribs[cc].sound.name = strdup("");
\r
1486 textAttribs[cc].sound.data = NULL;
\r
1488 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1489 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1490 sounds[sc].data = NULL;
\r
1491 MyLoadSound(&sounds[sc]);
\r
1496 SetCommPortDefaults()
\r
1498 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1499 dcb.DCBlength = sizeof(DCB);
\r
1500 dcb.BaudRate = 9600;
\r
1501 dcb.fBinary = TRUE;
\r
1502 dcb.fParity = FALSE;
\r
1503 dcb.fOutxCtsFlow = FALSE;
\r
1504 dcb.fOutxDsrFlow = FALSE;
\r
1505 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1506 dcb.fDsrSensitivity = FALSE;
\r
1507 dcb.fTXContinueOnXoff = TRUE;
\r
1508 dcb.fOutX = FALSE;
\r
1510 dcb.fNull = FALSE;
\r
1511 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1512 dcb.fAbortOnError = FALSE;
\r
1514 dcb.Parity = SPACEPARITY;
\r
1515 dcb.StopBits = ONESTOPBIT;
\r
1518 // [HGM] args: these three cases taken out to stay in front-end
\r
1520 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1521 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1522 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1523 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1525 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1526 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1527 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1528 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1529 ad->argName, mfp->faceName, mfp->pointSize,
\r
1530 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1531 mfp->bold ? "b" : "",
\r
1532 mfp->italic ? "i" : "",
\r
1533 mfp->underline ? "u" : "",
\r
1534 mfp->strikeout ? "s" : "",
\r
1535 (int)mfp->charset);
\r
1541 { // [HGM] copy the names from the internal WB variables to appData
\r
1544 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1545 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1546 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1547 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1551 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1552 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1553 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1554 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1555 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1556 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1557 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1558 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1559 (ta->effects) ? " " : "",
\r
1560 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1564 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1565 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1566 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1567 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1568 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1572 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1573 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1574 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1578 ParseCommPortSettings(char *s)
\r
1579 { // wrapper to keep dcb from back-end
\r
1580 ParseCommSettings(s, &dcb);
\r
1585 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1586 GetActualPlacement(hwndMain, &wpMain);
\r
1587 GetActualPlacement(hwndConsole, &wpConsole);
\r
1588 GetActualPlacement(commentDialog, &wpComment);
\r
1589 GetActualPlacement(editTagsDialog, &wpTags);
\r
1590 GetActualPlacement(gameListDialog, &wpGameList);
\r
1591 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1592 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1593 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1597 PrintCommPortSettings(FILE *f, char *name)
\r
1598 { // wrapper to shield back-end from DCB
\r
1599 PrintCommSettings(f, name, &dcb);
\r
1603 MySearchPath(char *installDir, char *name, char *fullname)
\r
1605 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1606 if(name[0]== '%') {
\r
1607 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1608 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1609 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1610 *strchr(buf, '%') = 0;
\r
1611 strcat(fullname, getenv(buf));
\r
1612 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1614 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1615 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1616 return (int) strlen(fullname);
\r
1618 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1622 MyGetFullPathName(char *name, char *fullname)
\r
1625 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1630 { // [HGM] args: allows testing if main window is realized from back-end
\r
1631 return hwndMain != NULL;
\r
1635 PopUpStartupDialog()
\r
1639 LoadLanguageFile(appData.language);
\r
1640 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1641 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1642 FreeProcInstance(lpProc);
\r
1645 /*---------------------------------------------------------------------------*\
\r
1647 * GDI board drawing routines
\r
1649 \*---------------------------------------------------------------------------*/
\r
1651 /* [AS] Draw square using background texture */
\r
1652 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1657 return; /* Should never happen! */
\r
1660 SetGraphicsMode( dst, GM_ADVANCED );
\r
1667 /* X reflection */
\r
1672 x.eDx = (FLOAT) dw + dx - 1;
\r
1675 SetWorldTransform( dst, &x );
\r
1678 /* Y reflection */
\r
1684 x.eDy = (FLOAT) dh + dy - 1;
\r
1686 SetWorldTransform( dst, &x );
\r
1694 x.eDx = (FLOAT) dx;
\r
1695 x.eDy = (FLOAT) dy;
\r
1698 SetWorldTransform( dst, &x );
\r
1702 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1710 SetWorldTransform( dst, &x );
\r
1712 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1715 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1717 PM_WP = (int) WhitePawn,
\r
1718 PM_WN = (int) WhiteKnight,
\r
1719 PM_WB = (int) WhiteBishop,
\r
1720 PM_WR = (int) WhiteRook,
\r
1721 PM_WQ = (int) WhiteQueen,
\r
1722 PM_WF = (int) WhiteFerz,
\r
1723 PM_WW = (int) WhiteWazir,
\r
1724 PM_WE = (int) WhiteAlfil,
\r
1725 PM_WM = (int) WhiteMan,
\r
1726 PM_WO = (int) WhiteCannon,
\r
1727 PM_WU = (int) WhiteUnicorn,
\r
1728 PM_WH = (int) WhiteNightrider,
\r
1729 PM_WA = (int) WhiteAngel,
\r
1730 PM_WC = (int) WhiteMarshall,
\r
1731 PM_WAB = (int) WhiteCardinal,
\r
1732 PM_WD = (int) WhiteDragon,
\r
1733 PM_WL = (int) WhiteLance,
\r
1734 PM_WS = (int) WhiteCobra,
\r
1735 PM_WV = (int) WhiteFalcon,
\r
1736 PM_WSG = (int) WhiteSilver,
\r
1737 PM_WG = (int) WhiteGrasshopper,
\r
1738 PM_WK = (int) WhiteKing,
\r
1739 PM_BP = (int) BlackPawn,
\r
1740 PM_BN = (int) BlackKnight,
\r
1741 PM_BB = (int) BlackBishop,
\r
1742 PM_BR = (int) BlackRook,
\r
1743 PM_BQ = (int) BlackQueen,
\r
1744 PM_BF = (int) BlackFerz,
\r
1745 PM_BW = (int) BlackWazir,
\r
1746 PM_BE = (int) BlackAlfil,
\r
1747 PM_BM = (int) BlackMan,
\r
1748 PM_BO = (int) BlackCannon,
\r
1749 PM_BU = (int) BlackUnicorn,
\r
1750 PM_BH = (int) BlackNightrider,
\r
1751 PM_BA = (int) BlackAngel,
\r
1752 PM_BC = (int) BlackMarshall,
\r
1753 PM_BG = (int) BlackGrasshopper,
\r
1754 PM_BAB = (int) BlackCardinal,
\r
1755 PM_BD = (int) BlackDragon,
\r
1756 PM_BL = (int) BlackLance,
\r
1757 PM_BS = (int) BlackCobra,
\r
1758 PM_BV = (int) BlackFalcon,
\r
1759 PM_BSG = (int) BlackSilver,
\r
1760 PM_BK = (int) BlackKing
\r
1763 static HFONT hPieceFont = NULL;
\r
1764 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1765 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1766 static int fontBitmapSquareSize = 0;
\r
1767 static char pieceToFontChar[(int) EmptySquare] =
\r
1768 { 'p', 'n', 'b', 'r', 'q',
\r
1769 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1770 'k', 'o', 'm', 'v', 't', 'w',
\r
1771 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1774 extern BOOL SetCharTable( char *table, const char * map );
\r
1775 /* [HGM] moved to backend.c */
\r
1777 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1780 BYTE r1 = GetRValue( color );
\r
1781 BYTE g1 = GetGValue( color );
\r
1782 BYTE b1 = GetBValue( color );
\r
1788 /* Create a uniform background first */
\r
1789 hbrush = CreateSolidBrush( color );
\r
1790 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1791 FillRect( hdc, &rc, hbrush );
\r
1792 DeleteObject( hbrush );
\r
1795 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1796 int steps = squareSize / 2;
\r
1799 for( i=0; i<steps; i++ ) {
\r
1800 BYTE r = r1 - (r1-r2) * i / steps;
\r
1801 BYTE g = g1 - (g1-g2) * i / steps;
\r
1802 BYTE b = b1 - (b1-b2) * i / steps;
\r
1804 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1805 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1806 FillRect( hdc, &rc, hbrush );
\r
1807 DeleteObject(hbrush);
\r
1810 else if( mode == 2 ) {
\r
1811 /* Diagonal gradient, good more or less for every piece */
\r
1812 POINT triangle[3];
\r
1813 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1814 HBRUSH hbrush_old;
\r
1815 int steps = squareSize;
\r
1818 triangle[0].x = squareSize - steps;
\r
1819 triangle[0].y = squareSize;
\r
1820 triangle[1].x = squareSize;
\r
1821 triangle[1].y = squareSize;
\r
1822 triangle[2].x = squareSize;
\r
1823 triangle[2].y = squareSize - steps;
\r
1825 for( i=0; i<steps; i++ ) {
\r
1826 BYTE r = r1 - (r1-r2) * i / steps;
\r
1827 BYTE g = g1 - (g1-g2) * i / steps;
\r
1828 BYTE b = b1 - (b1-b2) * i / steps;
\r
1830 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1831 hbrush_old = SelectObject( hdc, hbrush );
\r
1832 Polygon( hdc, triangle, 3 );
\r
1833 SelectObject( hdc, hbrush_old );
\r
1834 DeleteObject(hbrush);
\r
1839 SelectObject( hdc, hpen );
\r
1844 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1845 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1846 piece: follow the steps as explained below.
\r
1848 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1852 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1858 int backColor = whitePieceColor;
\r
1859 int foreColor = blackPieceColor;
\r
1861 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1862 backColor = appData.fontBackColorWhite;
\r
1863 foreColor = appData.fontForeColorWhite;
\r
1865 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1866 backColor = appData.fontBackColorBlack;
\r
1867 foreColor = appData.fontForeColorBlack;
\r
1871 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1873 hbm_old = SelectObject( hdc, hbm );
\r
1877 rc.right = squareSize;
\r
1878 rc.bottom = squareSize;
\r
1880 /* Step 1: background is now black */
\r
1881 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1883 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1885 pt.x = (squareSize - sz.cx) / 2;
\r
1886 pt.y = (squareSize - sz.cy) / 2;
\r
1888 SetBkMode( hdc, TRANSPARENT );
\r
1889 SetTextColor( hdc, chroma );
\r
1890 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1891 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1893 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1894 /* Step 3: the area outside the piece is filled with white */
\r
1895 // FloodFill( hdc, 0, 0, chroma );
\r
1896 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1897 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1898 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1899 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1900 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1902 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1903 but if the start point is not inside the piece we're lost!
\r
1904 There should be a better way to do this... if we could create a region or path
\r
1905 from the fill operation we would be fine for example.
\r
1907 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1908 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1910 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1911 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1912 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1914 SelectObject( dc2, bm2 );
\r
1915 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1916 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1917 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1918 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1919 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1922 DeleteObject( bm2 );
\r
1925 SetTextColor( hdc, 0 );
\r
1927 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1928 draw the piece again in black for safety.
\r
1930 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1932 SelectObject( hdc, hbm_old );
\r
1934 if( hPieceMask[index] != NULL ) {
\r
1935 DeleteObject( hPieceMask[index] );
\r
1938 hPieceMask[index] = hbm;
\r
1941 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1943 SelectObject( hdc, hbm );
\r
1946 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1947 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1948 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1950 SelectObject( dc1, hPieceMask[index] );
\r
1951 SelectObject( dc2, bm2 );
\r
1952 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1953 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1956 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1957 the piece background and deletes (makes transparent) the rest.
\r
1958 Thanks to that mask, we are free to paint the background with the greates
\r
1959 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1960 We use this, to make gradients and give the pieces a "roundish" look.
\r
1962 SetPieceBackground( hdc, backColor, 2 );
\r
1963 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1967 DeleteObject( bm2 );
\r
1970 SetTextColor( hdc, foreColor );
\r
1971 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1973 SelectObject( hdc, hbm_old );
\r
1975 if( hPieceFace[index] != NULL ) {
\r
1976 DeleteObject( hPieceFace[index] );
\r
1979 hPieceFace[index] = hbm;
\r
1982 static int TranslatePieceToFontPiece( int piece )
\r
2012 case BlackMarshall:
\r
2016 case BlackNightrider:
\r
2022 case BlackUnicorn:
\r
2026 case BlackGrasshopper:
\r
2038 case BlackCardinal:
\r
2045 case WhiteMarshall:
\r
2049 case WhiteNightrider:
\r
2055 case WhiteUnicorn:
\r
2059 case WhiteGrasshopper:
\r
2071 case WhiteCardinal:
\r
2080 void CreatePiecesFromFont()
\r
2083 HDC hdc_window = NULL;
\r
2089 if( fontBitmapSquareSize < 0 ) {
\r
2090 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
2094 if( !appData.useFont || appData.renderPiecesWithFont == NULL ||
\r
2095 appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
2096 fontBitmapSquareSize = -1;
\r
2100 if( fontBitmapSquareSize != squareSize ) {
\r
2101 hdc_window = GetDC( hwndMain );
\r
2102 hdc = CreateCompatibleDC( hdc_window );
\r
2104 if( hPieceFont != NULL ) {
\r
2105 DeleteObject( hPieceFont );
\r
2108 for( i=0; i<=(int)BlackKing; i++ ) {
\r
2109 hPieceMask[i] = NULL;
\r
2110 hPieceFace[i] = NULL;
\r
2116 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
2117 fontHeight = appData.fontPieceSize;
\r
2120 fontHeight = (fontHeight * squareSize) / 100;
\r
2122 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2124 lf.lfEscapement = 0;
\r
2125 lf.lfOrientation = 0;
\r
2126 lf.lfWeight = FW_NORMAL;
\r
2128 lf.lfUnderline = 0;
\r
2129 lf.lfStrikeOut = 0;
\r
2130 lf.lfCharSet = DEFAULT_CHARSET;
\r
2131 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2132 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2133 lf.lfQuality = PROOF_QUALITY;
\r
2134 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2135 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2136 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2138 hPieceFont = CreateFontIndirect( &lf );
\r
2140 if( hPieceFont == NULL ) {
\r
2141 fontBitmapSquareSize = -2;
\r
2144 /* Setup font-to-piece character table */
\r
2145 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2146 /* No (or wrong) global settings, try to detect the font */
\r
2147 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2149 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2151 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2152 /* DiagramTT* family */
\r
2153 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2155 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2156 /* Fairy symbols */
\r
2157 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2159 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2160 /* Good Companion (Some characters get warped as literal :-( */
\r
2161 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2162 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2163 SetCharTable(pieceToFontChar, s);
\r
2166 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2167 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2171 /* Create bitmaps */
\r
2172 hfont_old = SelectObject( hdc, hPieceFont );
\r
2173 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2174 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2175 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2177 SelectObject( hdc, hfont_old );
\r
2179 fontBitmapSquareSize = squareSize;
\r
2183 if( hdc != NULL ) {
\r
2187 if( hdc_window != NULL ) {
\r
2188 ReleaseDC( hwndMain, hdc_window );
\r
2193 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2195 char name[128], buf[MSG_SIZ];
\r
2197 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2198 if(appData.pieceDirectory[0]) {
\r
2200 snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);
\r
2201 res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
2202 if(res) return res;
\r
2204 if (gameInfo.event &&
\r
2205 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2206 strcmp(name, "k80s") == 0) {
\r
2207 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2209 return LoadBitmap(hinst, name);
\r
2213 /* Insert a color into the program's logical palette
\r
2214 structure. This code assumes the given color is
\r
2215 the result of the RGB or PALETTERGB macro, and it
\r
2216 knows how those macros work (which is documented).
\r
2219 InsertInPalette(COLORREF color)
\r
2221 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2223 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2224 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2225 pLogPal->palNumEntries--;
\r
2229 pe->peFlags = (char) 0;
\r
2230 pe->peRed = (char) (0xFF & color);
\r
2231 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2232 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2238 InitDrawingColors()
\r
2241 if (pLogPal == NULL) {
\r
2242 /* Allocate enough memory for a logical palette with
\r
2243 * PALETTESIZE entries and set the size and version fields
\r
2244 * of the logical palette structure.
\r
2246 pLogPal = (NPLOGPALETTE)
\r
2247 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2248 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2249 pLogPal->palVersion = 0x300;
\r
2251 pLogPal->palNumEntries = 0;
\r
2253 InsertInPalette(lightSquareColor);
\r
2254 InsertInPalette(darkSquareColor);
\r
2255 InsertInPalette(whitePieceColor);
\r
2256 InsertInPalette(blackPieceColor);
\r
2257 InsertInPalette(highlightSquareColor);
\r
2258 InsertInPalette(premoveHighlightColor);
\r
2260 /* create a logical color palette according the information
\r
2261 * in the LOGPALETTE structure.
\r
2263 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2265 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2266 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2267 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2268 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2269 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2270 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2271 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2272 for(i=0; i<8;i++) markerBrush[i] = CreateSolidBrush(markerColor[i]); // [HGM] markers
\r
2274 /* [AS] Force rendering of the font-based pieces */
\r
2275 if( fontBitmapSquareSize > 0 ) {
\r
2276 fontBitmapSquareSize = 0;
\r
2282 BoardWidth(int boardSize, int n)
\r
2283 { /* [HGM] argument n added to allow different width and height */
\r
2284 int lineGap = sizeInfo[boardSize].lineGap;
\r
2286 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2287 lineGap = appData.overrideLineGap;
\r
2290 return (n + 1) * lineGap +
\r
2291 n * sizeInfo[boardSize].squareSize;
\r
2294 /* Respond to board resize by dragging edge */
\r
2296 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2298 BoardSize newSize = NUM_SIZES - 1;
\r
2299 static int recurse = 0;
\r
2300 if (IsIconic(hwndMain)) return;
\r
2301 if (recurse > 0) return;
\r
2303 while (newSize > 0) {
\r
2304 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2305 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2306 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2309 boardSize = newSize;
\r
2310 InitDrawingSizes(boardSize, flags);
\r
2315 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2318 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2320 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2321 ChessSquare piece;
\r
2322 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2324 SIZE clockSize, messageSize;
\r
2326 char buf[MSG_SIZ];
\r
2328 HMENU hmenu = GetMenu(hwndMain);
\r
2329 RECT crect, wrect, oldRect;
\r
2331 LOGBRUSH logbrush;
\r
2332 VariantClass v = gameInfo.variant;
\r
2334 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2335 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2337 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2338 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2339 if(boardSize == -1) return; // no size defined yet; abort (to allow early call of InitPosition)
\r
2340 oldBoardSize = boardSize;
\r
2342 if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)
\r
2343 { // correct board size to one where built-in pieces exist
\r
2344 if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)
\r
2345 && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range
\r
2347 || (v == VariantShogi && boardSize != SizeModerate) // Japanese-style Shogi
\r
2348 || v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan
\r
2349 || v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy || v == VariantLion ) {
\r
2350 if(boardSize < SizeMediocre) boardSize = SizePetite; else
\r
2351 if(boardSize > SizeModerate) boardSize = SizeBulky; else
\r
2352 boardSize = SizeMiddling;
\r
2355 if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite
\r
2357 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2358 oldRect.top = wpMain.y;
\r
2359 oldRect.right = wpMain.x + wpMain.width;
\r
2360 oldRect.bottom = wpMain.y + wpMain.height;
\r
2362 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2363 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2364 squareSize = sizeInfo[boardSize].squareSize;
\r
2365 lineGap = sizeInfo[boardSize].lineGap;
\r
2366 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2367 border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;
\r
2369 // [HGM] decide on tininess based on total board width rather than square size
\r
2370 tinyLayout = squareSize * (BOARD_WIDTH);
\r
2371 tinyLayout = tinyLayout < 35*8 ? 2 : tinyLayout < 43*8 ? 1 : 0;
\r
2373 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2374 lineGap = appData.overrideLineGap;
\r
2377 if (tinyLayout != oldTinyLayout) {
\r
2378 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2379 if (tinyLayout == 2) {
\r
2380 style &= ~WS_SYSMENU;
\r
2381 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2382 "&Minimize\tCtrl+F4");
\r
2384 style |= WS_SYSMENU;
\r
2385 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2387 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2389 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2390 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2391 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2393 DrawMenuBar(hwndMain);
\r
2396 boardWidth = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;
\r
2397 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;
\r
2399 /* Get text area sizes */
\r
2400 hdc = GetDC(hwndMain);
\r
2401 if (appData.clockMode) {
\r
2402 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2404 snprintf(buf, MSG_SIZ, _("White"));
\r
2406 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2407 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2408 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2409 str = _("We only care about the height here");
\r
2410 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2411 SelectObject(hdc, oldFont);
\r
2412 ReleaseDC(hwndMain, hdc);
\r
2414 /* Compute where everything goes */
\r
2415 if((first.programLogo || second.programLogo) && tinyLayout != 2) {
\r
2416 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2417 logoHeight = 2*clockSize.cy;
\r
2418 leftLogoRect.left = OUTER_MARGIN;
\r
2419 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2420 leftLogoRect.top = OUTER_MARGIN;
\r
2421 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2423 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2424 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2425 rightLogoRect.top = OUTER_MARGIN;
\r
2426 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2429 whiteRect.left = leftLogoRect.right;
\r
2430 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2431 whiteRect.top = OUTER_MARGIN;
\r
2432 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2434 blackRect.right = rightLogoRect.left;
\r
2435 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2436 blackRect.top = whiteRect.top;
\r
2437 blackRect.bottom = whiteRect.bottom;
\r
2439 whiteRect.left = OUTER_MARGIN;
\r
2440 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2441 whiteRect.top = OUTER_MARGIN;
\r
2442 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2444 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2445 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2446 blackRect.top = whiteRect.top;
\r
2447 blackRect.bottom = whiteRect.bottom;
\r
2449 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2452 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2453 if (appData.showButtonBar) {
\r
2454 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2455 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2457 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2459 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2460 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2462 boardRect.left = OUTER_MARGIN;
\r
2463 boardRect.right = boardRect.left + boardWidth;
\r
2464 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2465 boardRect.bottom = boardRect.top + boardHeight;
\r
2467 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2468 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2469 oldTinyLayout = tinyLayout;
\r
2470 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2471 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2472 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2473 winW *= 1 + twoBoards;
\r
2474 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2475 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2476 wpMain.height = winH; // without disturbing window attachments
\r
2477 GetWindowRect(hwndMain, &wrect);
\r
2478 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2479 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2481 // [HGM] placement: let attached windows follow size change.
\r
2482 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2483 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2484 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2485 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2486 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2488 /* compensate if menu bar wrapped */
\r
2489 GetClientRect(hwndMain, &crect);
\r
2490 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2491 wpMain.height += offby;
\r
2493 case WMSZ_TOPLEFT:
\r
2494 SetWindowPos(hwndMain, NULL,
\r
2495 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2496 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2499 case WMSZ_TOPRIGHT:
\r
2501 SetWindowPos(hwndMain, NULL,
\r
2502 wrect.left, wrect.bottom - wpMain.height,
\r
2503 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2506 case WMSZ_BOTTOMLEFT:
\r
2508 SetWindowPos(hwndMain, NULL,
\r
2509 wrect.right - wpMain.width, wrect.top,
\r
2510 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2513 case WMSZ_BOTTOMRIGHT:
\r
2517 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2518 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2523 for (i = 0; i < N_BUTTONS; i++) {
\r
2524 if (buttonDesc[i].hwnd != NULL) {
\r
2525 DestroyWindow(buttonDesc[i].hwnd);
\r
2526 buttonDesc[i].hwnd = NULL;
\r
2528 if (appData.showButtonBar) {
\r
2529 buttonDesc[i].hwnd =
\r
2530 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2531 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2532 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2533 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2534 (HMENU) buttonDesc[i].id,
\r
2535 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2536 if (tinyLayout == 2) {
\r
2537 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2538 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2539 MAKELPARAM(FALSE, 0));
\r
2541 if (buttonDesc[i].id == IDM_Pause)
\r
2542 hwndPause = buttonDesc[i].hwnd;
\r
2543 buttonDesc[i].wndproc = (WNDPROC)
\r
2544 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2547 if (gridPen != NULL) DeleteObject(gridPen);
\r
2548 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2549 if (premovePen != NULL) DeleteObject(premovePen);
\r
2550 if (lineGap != 0) {
\r
2551 logbrush.lbStyle = BS_SOLID;
\r
2552 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2554 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2555 lineGap, &logbrush, 0, NULL);
\r
2556 logbrush.lbColor = highlightSquareColor;
\r
2558 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2559 lineGap, &logbrush, 0, NULL);
\r
2561 logbrush.lbColor = premoveHighlightColor;
\r
2563 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2564 lineGap, &logbrush, 0, NULL);
\r
2566 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2567 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2568 gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;
\r
2569 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2570 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;
\r
2571 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2572 BOARD_WIDTH * (squareSize + lineGap) + border;
\r
2573 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2575 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2576 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;
\r
2577 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2578 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2579 lineGap / 2 + (i * (squareSize + lineGap)) + border;
\r
2580 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2581 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;
\r
2582 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2586 /* [HGM] Licensing requirement */
\r
2588 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2591 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2593 GothicPopUp( "", VariantNormal);
\r
2596 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2598 /* Load piece bitmaps for this board size */
\r
2599 for (i=0; i<=2; i++) {
\r
2600 for (piece = WhitePawn;
\r
2601 (int) piece < (int) BlackPawn;
\r
2602 piece = (ChessSquare) ((int) piece + 1)) {
\r
2603 if (pieceBitmap[i][piece] != NULL)
\r
2604 DeleteObject(pieceBitmap[i][piece]);
\r
2605 pieceBitmap[i][piece] = NULL;
\r
2609 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2611 // Orthodox Chess pieces
\r
2612 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2613 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2614 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2615 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2616 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2617 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2618 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2619 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2620 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2621 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2622 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2623 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2624 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2625 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2626 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2627 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2628 // in Shogi, Hijack the unused Queen for Lance
\r
2629 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2630 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2631 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2633 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2634 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2635 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2638 if(squareSize <= 72 && squareSize >= 33) {
\r
2639 /* A & C are available in most sizes now */
\r
2640 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2641 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2642 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2643 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2644 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2645 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2646 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2647 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2648 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2649 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2650 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2651 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2652 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2653 } else { // Smirf-like
\r
2654 if(gameInfo.variant == VariantSChess) {
\r
2655 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2656 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2657 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2659 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2660 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2661 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2664 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2665 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2666 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2667 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2668 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2669 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2670 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2671 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2672 } else { // WinBoard standard
\r
2673 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2674 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2675 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2680 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2681 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2682 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2683 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2684 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2685 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2686 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2687 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2688 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2689 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2690 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2691 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2692 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2693 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2694 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2695 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2696 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2697 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2698 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2699 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2700 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2701 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2702 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2703 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2704 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2705 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2706 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2707 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2708 pieceBitmap[0][WhiteAmazon] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2709 pieceBitmap[1][WhiteAmazon] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2710 pieceBitmap[2][WhiteAmazon] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2711 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2712 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2713 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2714 pieceBitmap[0][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "s");
\r
2715 pieceBitmap[1][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "o");
\r
2716 pieceBitmap[2][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "w");
\r
2717 pieceBitmap[0][WhiteCub] = DoLoadBitmap(hInst, "ln", squareSize, "s");
\r
2718 pieceBitmap[1][WhiteCub] = DoLoadBitmap(hInst, "ln", squareSize, "o");
\r
2719 pieceBitmap[2][WhiteCub] = DoLoadBitmap(hInst, "ln", squareSize, "w");
\r
2720 pieceBitmap[0][WhiteWolf] = DoLoadBitmap(hInst, "wolf", squareSize, "s");
\r
2721 pieceBitmap[1][WhiteWolf] = DoLoadBitmap(hInst, "wolf", squareSize, "o");
\r
2722 pieceBitmap[2][WhiteWolf] = DoLoadBitmap(hInst, "wolf", squareSize, "w");
\r
2723 pieceBitmap[0][WhiteCamel] = DoLoadBitmap(hInst, "camel", squareSize, "s");
\r
2724 pieceBitmap[1][WhiteCamel] = DoLoadBitmap(hInst, "camel", squareSize, "o");
\r
2725 pieceBitmap[2][WhiteCamel] = DoLoadBitmap(hInst, "camel", squareSize, "w");
\r
2726 pieceBitmap[0][WhiteZebra] = DoLoadBitmap(hInst, "zebra", squareSize, "s");
\r
2727 pieceBitmap[1][WhiteZebra] = DoLoadBitmap(hInst, "zebra", squareSize, "o");
\r
2728 pieceBitmap[2][WhiteZebra] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2730 if(gameInfo.variant == VariantShogi && BOARD_HEIGHT != 7) { /* promoted Gold representations (but not in Tori!)*/
\r
2731 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2732 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2733 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2734 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2735 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2736 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2737 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2738 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2739 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2740 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2741 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2742 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2744 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2745 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2746 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2747 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2748 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2749 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2750 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2751 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2752 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2753 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2754 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2755 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2758 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2759 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2760 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2761 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2762 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2763 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2764 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2765 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2766 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2767 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2768 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2769 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2770 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2771 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2772 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2776 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2777 /* special Shogi support in this size */
\r
2778 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2779 for (piece = WhitePawn;
\r
2780 (int) piece < (int) BlackPawn;
\r
2781 piece = (ChessSquare) ((int) piece + 1)) {
\r
2782 if (pieceBitmap[i][piece] != NULL)
\r
2783 DeleteObject(pieceBitmap[i][piece]);
\r
2786 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2787 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2788 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2789 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2790 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2791 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2792 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2793 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2794 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2795 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2796 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2797 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2798 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2799 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2800 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2801 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2802 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2803 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2804 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2805 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2806 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2807 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2808 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2809 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2810 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2811 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2812 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2813 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2814 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2815 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2816 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2817 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2818 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2819 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2820 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2821 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2822 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2823 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2824 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2825 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2826 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2827 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2831 if(appData.pieceDirectory[0]) for(i=WhitePawn; i<BlackPawn; i++) { // try for all missing pieces with new naming convention
\r
2832 char buf[MSG_SIZ];
\r
2833 if(pieceBitmap[0][i]) continue;
\r
2834 snprintf(buf, MSG_SIZ, "piece%d_", i);
\r
2835 pieceBitmap[0][i] = DoLoadBitmap(hInst, buf, squareSize, "s");
\r
2836 pieceBitmap[1][i] = DoLoadBitmap(hInst, buf, squareSize, "o");
\r
2837 pieceBitmap[2][i] = DoLoadBitmap(hInst, buf, squareSize, "w");
\r
2842 PieceBitmap(ChessSquare p, int kind)
\r
2844 if ((int) p >= (int) BlackPawn)
\r
2845 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2847 return pieceBitmap[kind][(int) p];
\r
2850 /***************************************************************/
\r
2852 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2853 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2855 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2856 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2860 SquareToPos(int row, int column, int * x, int * y)
\r
2863 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
2864 *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;
\r
2866 *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;
\r
2867 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
2872 DrawCoordsOnDC(HDC hdc)
\r
2874 static char files[] = "0123456789012345678901221098765432109876543210";
\r
2875 static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";
\r
2876 char str[2] = { NULLCHAR, NULLCHAR };
\r
2877 int oldMode, oldAlign, x, y, start, i;
\r
2881 if (!appData.showCoords)
\r
2884 start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;
\r
2886 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2887 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2888 oldAlign = GetTextAlign(hdc);
\r
2889 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2891 y = boardRect.top + lineGap;
\r
2892 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2895 SetTextAlign(hdc, TA_RIGHT|TA_TOP);
\r
2896 x += border - lineGap - 4; y += squareSize - 6;
\r
2898 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2899 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2900 str[0] = files[start + i];
\r
2901 ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);
\r
2902 y += squareSize + lineGap;
\r
2905 start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;
\r
2908 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2909 x += -border + 4; y += border - squareSize + 6;
\r
2911 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2912 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2913 str[0] = ranks[start + i];
\r
2914 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2915 x += squareSize + lineGap;
\r
2918 SelectObject(hdc, oldBrush);
\r
2919 SetBkMode(hdc, oldMode);
\r
2920 SetTextAlign(hdc, oldAlign);
\r
2921 SelectObject(hdc, oldFont);
\r
2925 DrawGridOnDC(HDC hdc)
\r
2929 if (lineGap != 0) {
\r
2930 oldPen = SelectObject(hdc, gridPen);
\r
2931 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2932 SelectObject(hdc, oldPen);
\r
2936 #define HIGHLIGHT_PEN 0
\r
2937 #define PREMOVE_PEN 1
\r
2940 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2943 HPEN oldPen, hPen;
\r
2944 if (lineGap == 0) return;
\r
2946 x1 = boardRect.left +
\r
2947 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;
\r
2948 y1 = boardRect.top +
\r
2949 lineGap/2 + y * (squareSize + lineGap) + border;
\r
2951 x1 = boardRect.left +
\r
2952 lineGap/2 + x * (squareSize + lineGap) + border;
\r
2953 y1 = boardRect.top +
\r
2954 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;
\r
2956 hPen = pen ? premovePen : highlightPen;
\r
2957 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2958 MoveToEx(hdc, x1, y1, NULL);
\r
2959 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2960 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2961 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2962 LineTo(hdc, x1, y1);
\r
2963 SelectObject(hdc, oldPen);
\r
2967 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2970 for (i=0; i<2; i++) {
\r
2971 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2972 DrawHighlightOnDC(hdc, TRUE,
\r
2973 h->sq[i].x, h->sq[i].y,
\r
2978 /* Note: sqcolor is used only in monoMode */
\r
2979 /* Note that this code is largely duplicated in woptions.c,
\r
2980 function DrawSampleSquare, so that needs to be updated too */
\r
2982 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2984 HBITMAP oldBitmap;
\r
2988 if (appData.blindfold) return;
\r
2990 /* [AS] Use font-based pieces if needed */
\r
2991 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2992 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2993 CreatePiecesFromFont();
\r
2995 if( fontBitmapSquareSize == squareSize ) {
\r
2996 int index = TranslatePieceToFontPiece(piece);
\r
2998 SelectObject( tmphdc, hPieceMask[ index ] );
\r
3000 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
3001 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
3005 squareSize, squareSize,
\r
3010 SelectObject( tmphdc, hPieceFace[ index ] );
\r
3012 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
3013 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
3017 squareSize, squareSize,
\r
3026 if (appData.monoMode) {
\r
3027 SelectObject(tmphdc, PieceBitmap(piece,
\r
3028 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
3029 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
3030 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
3032 HBRUSH xBrush = whitePieceBrush;
\r
3033 tmpSize = squareSize;
\r
3034 if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);
\r
3036 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
3037 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
3038 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
3039 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
3040 x += (squareSize - minorSize)>>1;
\r
3041 y += squareSize - minorSize - 2;
\r
3042 tmpSize = minorSize;
\r
3044 if (color || appData.allWhite ) {
\r
3045 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
3047 oldBrush = SelectObject(hdc, xBrush);
\r
3048 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
3049 if(appData.upsideDown && color==flipView)
\r
3050 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
3052 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
3053 /* Use black for outline of white pieces */
\r
3054 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
3055 if(appData.upsideDown && color==flipView)
\r
3056 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
3058 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
3059 } else if(appData.pieceDirectory[0]) {
\r
3060 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
3061 oldBrush = SelectObject(hdc, xBrush);
\r
3062 if(appData.upsideDown && color==flipView)
\r
3063 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
3065 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
3066 SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
3067 if(appData.upsideDown && color==flipView)
\r
3068 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
3070 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
3072 /* Use square color for details of black pieces */
\r
3073 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
3074 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
3075 if(appData.upsideDown && !flipView)
\r
3076 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
3078 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
3080 SelectObject(hdc, oldBrush);
\r
3081 SelectObject(tmphdc, oldBitmap);
\r
3085 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
3086 int GetBackTextureMode( int algo )
\r
3088 int result = BACK_TEXTURE_MODE_DISABLED;
\r
3092 case BACK_TEXTURE_MODE_PLAIN:
\r
3093 result = 1; /* Always use identity map */
\r
3095 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
3096 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
3104 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
3105 to handle redraws cleanly (as random numbers would always be different).
\r
3107 VOID RebuildTextureSquareInfo()
\r
3117 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
3119 if( liteBackTexture != NULL ) {
\r
3120 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3121 lite_w = bi.bmWidth;
\r
3122 lite_h = bi.bmHeight;
\r
3126 if( darkBackTexture != NULL ) {
\r
3127 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3128 dark_w = bi.bmWidth;
\r
3129 dark_h = bi.bmHeight;
\r
3133 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
3134 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
3135 if( (col + row) & 1 ) {
\r
3137 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
3138 if( lite_w >= squareSize*BOARD_WIDTH )
\r
3139 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
3141 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
3142 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
3143 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
3145 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
3146 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
3151 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
3152 if( dark_w >= squareSize*BOARD_WIDTH )
\r
3153 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
3155 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
3156 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
3157 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
3159 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
3160 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
3167 /* [AS] Arrow highlighting support */
\r
3169 static double A_WIDTH = 5; /* Width of arrow body */
\r
3171 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
3172 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
3174 static double Sqr( double x )
\r
3179 static int Round( double x )
\r
3181 return (int) (x + 0.5);
\r
3184 /* Draw an arrow between two points using current settings */
\r
3185 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
3188 double dx, dy, j, k, x, y;
\r
3190 if( d_x == s_x ) {
\r
3191 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3193 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
3196 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
3197 arrow[1].y = d_y - h;
\r
3199 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3200 arrow[2].y = d_y - h;
\r
3205 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3206 arrow[5].y = d_y - h;
\r
3208 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3209 arrow[4].y = d_y - h;
\r
3211 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3214 else if( d_y == s_y ) {
\r
3215 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3218 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3220 arrow[1].x = d_x - w;
\r
3221 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3223 arrow[2].x = d_x - w;
\r
3224 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3229 arrow[5].x = d_x - w;
\r
3230 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3232 arrow[4].x = d_x - w;
\r
3233 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3236 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3239 /* [AS] Needed a lot of paper for this! :-) */
\r
3240 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3241 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3243 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3245 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3250 arrow[0].x = Round(x - j);
\r
3251 arrow[0].y = Round(y + j*dx);
\r
3253 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3254 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3257 x = (double) d_x - k;
\r
3258 y = (double) d_y - k*dy;
\r
3261 x = (double) d_x + k;
\r
3262 y = (double) d_y + k*dy;
\r
3265 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3267 arrow[6].x = Round(x - j);
\r
3268 arrow[6].y = Round(y + j*dx);
\r
3270 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3271 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3273 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3274 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3279 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3280 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3283 Polygon( hdc, arrow, 7 );
\r
3286 /* [AS] Draw an arrow between two squares */
\r
3287 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3289 int s_x, s_y, d_x, d_y;
\r
3296 if( s_col == d_col && s_row == d_row ) {
\r
3300 /* Get source and destination points */
\r
3301 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3302 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3305 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3307 else if( d_y < s_y ) {
\r
3308 d_y += squareSize / 2 + squareSize / 4;
\r
3311 d_y += squareSize / 2;
\r
3315 d_x += squareSize / 2 - squareSize / 4;
\r
3317 else if( d_x < s_x ) {
\r
3318 d_x += squareSize / 2 + squareSize / 4;
\r
3321 d_x += squareSize / 2;
\r
3324 s_x += squareSize / 2;
\r
3325 s_y += squareSize / 2;
\r
3327 /* Adjust width */
\r
3328 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3331 stLB.lbStyle = BS_SOLID;
\r
3332 stLB.lbColor = appData.highlightArrowColor;
\r
3335 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3336 holdpen = SelectObject( hdc, hpen );
\r
3337 hbrush = CreateBrushIndirect( &stLB );
\r
3338 holdbrush = SelectObject( hdc, hbrush );
\r
3340 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3342 SelectObject( hdc, holdpen );
\r
3343 SelectObject( hdc, holdbrush );
\r
3344 DeleteObject( hpen );
\r
3345 DeleteObject( hbrush );
\r
3348 BOOL HasHighlightInfo()
\r
3350 BOOL result = FALSE;
\r
3352 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3353 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3364 BOOL IsDrawArrowEnabled()
\r
3366 BOOL result = FALSE;
\r
3368 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3375 VOID DrawArrowHighlight( HDC hdc )
\r
3377 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3378 DrawArrowBetweenSquares( hdc,
\r
3379 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3380 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3384 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3386 HRGN result = NULL;
\r
3388 if( HasHighlightInfo() ) {
\r
3389 int x1, y1, x2, y2;
\r
3390 int sx, sy, dx, dy;
\r
3392 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3393 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3395 sx = MIN( x1, x2 );
\r
3396 sy = MIN( y1, y2 );
\r
3397 dx = MAX( x1, x2 ) + squareSize;
\r
3398 dy = MAX( y1, y2 ) + squareSize;
\r
3400 result = CreateRectRgn( sx, sy, dx, dy );
\r
3407 Warning: this function modifies the behavior of several other functions.
\r
3409 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3410 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3411 repaint is scattered all over the place, which is not good for features such as
\r
3412 "arrow highlighting" that require a full repaint of the board.
\r
3414 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3415 user interaction, when speed is not so important) but especially to avoid errors
\r
3416 in the displayed graphics.
\r
3418 In such patched places, I always try refer to this function so there is a single
\r
3419 place to maintain knowledge.
\r
3421 To restore the original behavior, just return FALSE unconditionally.
\r
3423 BOOL IsFullRepaintPreferrable()
\r
3425 BOOL result = FALSE;
\r
3427 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3428 /* Arrow may appear on the board */
\r
3436 This function is called by DrawPosition to know whether a full repaint must
\r
3439 Only DrawPosition may directly call this function, which makes use of
\r
3440 some state information. Other function should call DrawPosition specifying
\r
3441 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3443 BOOL DrawPositionNeedsFullRepaint()
\r
3445 BOOL result = FALSE;
\r
3448 Probably a slightly better policy would be to trigger a full repaint
\r
3449 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3450 but animation is fast enough that it's difficult to notice.
\r
3452 if( animInfo.piece == EmptySquare ) {
\r
3453 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3461 static HBITMAP borderBitmap;
\r
3464 DrawBackgroundOnDC(HDC hdc)
\r
3470 static char oldBorder[MSG_SIZ];
\r
3471 int w = 600, h = 600, mode;
\r
3473 if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid
\r
3474 strncpy(oldBorder, appData.border, MSG_SIZ-1);
\r
3475 borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
3477 if(borderBitmap == NULL) { // loading failed, use white
\r
3478 FillRect( hdc, &boardRect, whitePieceBrush );
\r
3481 tmphdc = CreateCompatibleDC(hdc);
\r
3482 hbm = SelectObject(tmphdc, borderBitmap);
\r
3483 if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {
\r
3487 mode = SetStretchBltMode(hdc, COLORONCOLOR);
\r
3488 StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left,
\r
3489 boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3490 SetStretchBltMode(hdc, mode);
\r
3491 SelectObject(tmphdc, hbm);
\r
3496 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3498 int row, column, x, y, square_color, piece_color;
\r
3499 ChessSquare piece;
\r
3501 HDC texture_hdc = NULL;
\r
3503 /* [AS] Initialize background textures if needed */
\r
3504 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3505 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3506 if( backTextureSquareSize != squareSize
\r
3507 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3508 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3509 backTextureSquareSize = squareSize;
\r
3510 RebuildTextureSquareInfo();
\r
3513 texture_hdc = CreateCompatibleDC( hdc );
\r
3516 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3517 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3519 SquareToPos(row, column, &x, &y);
\r
3521 piece = board[row][column];
\r
3523 square_color = ((column + row) % 2) == 1;
\r
3524 if( gameInfo.variant == VariantXiangqi ) {
\r
3525 square_color = !InPalace(row, column);
\r
3526 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3527 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3529 piece_color = (int) piece < (int) BlackPawn;
\r
3532 /* [HGM] holdings file: light square or black */
\r
3533 if(column == BOARD_LEFT-2) {
\r
3534 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3537 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3541 if(column == BOARD_RGHT + 1 ) {
\r
3542 if( row < gameInfo.holdingsSize )
\r
3545 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3549 if(column == BOARD_LEFT-1 ) /* left align */
\r
3550 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3551 else if( column == BOARD_RGHT) /* right align */
\r
3552 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3553 else if( piece == DarkSquare) DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3555 if (appData.monoMode) {
\r
3556 if (piece == EmptySquare) {
\r
3557 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3558 square_color ? WHITENESS : BLACKNESS);
\r
3560 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3563 else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {
\r
3564 /* [AS] Draw the square using a texture bitmap */
\r
3565 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3566 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3567 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3570 squareSize, squareSize,
\r
3573 backTextureSquareInfo[r][c].mode,
\r
3574 backTextureSquareInfo[r][c].x,
\r
3575 backTextureSquareInfo[r][c].y );
\r
3577 SelectObject( texture_hdc, hbm );
\r
3579 if (piece != EmptySquare) {
\r
3580 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3584 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3586 oldBrush = SelectObject(hdc, brush );
\r
3587 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3588 SelectObject(hdc, oldBrush);
\r
3589 if (piece != EmptySquare)
\r
3590 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3595 if( texture_hdc != NULL ) {
\r
3596 DeleteDC( texture_hdc );
\r
3600 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3601 void fputDW(FILE *f, int x)
\r
3603 fputc(x & 255, f);
\r
3604 fputc(x>>8 & 255, f);
\r
3605 fputc(x>>16 & 255, f);
\r
3606 fputc(x>>24 & 255, f);
\r
3609 #define MAX_CLIPS 200 /* more than enough */
\r
3612 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3614 // HBITMAP bufferBitmap;
\r
3619 int w = 100, h = 50;
\r
3621 if(logo == NULL) {
\r
3622 if(!logoHeight) return;
\r
3623 FillRect( hdc, &logoRect, whitePieceBrush );
\r
3625 // GetClientRect(hwndMain, &Rect);
\r
3626 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3627 // Rect.bottom-Rect.top+1);
\r
3628 tmphdc = CreateCompatibleDC(hdc);
\r
3629 hbm = SelectObject(tmphdc, logo);
\r
3630 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3634 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3635 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3636 SelectObject(tmphdc, hbm);
\r
3644 HDC hdc = GetDC(hwndMain);
\r
3645 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3646 if(appData.autoLogo) {
\r
3648 switch(gameMode) { // pick logos based on game mode
\r
3649 case IcsObserving:
\r
3650 whiteLogo = second.programLogo; // ICS logo
\r
3651 blackLogo = second.programLogo;
\r
3654 case IcsPlayingWhite:
\r
3655 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3656 blackLogo = second.programLogo; // ICS logo
\r
3658 case IcsPlayingBlack:
\r
3659 whiteLogo = second.programLogo; // ICS logo
\r
3660 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3662 case TwoMachinesPlay:
\r
3663 if(first.twoMachinesColor[0] == 'b') {
\r
3664 whiteLogo = second.programLogo;
\r
3665 blackLogo = first.programLogo;
\r
3668 case MachinePlaysWhite:
\r
3669 blackLogo = userLogo;
\r
3671 case MachinePlaysBlack:
\r
3672 whiteLogo = userLogo;
\r
3673 blackLogo = first.programLogo;
\r
3676 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3677 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3678 ReleaseDC(hwndMain, hdc);
\r
3683 UpdateLogos(int display)
\r
3684 { // called after loading new engine(s), in tourney or from menu
\r
3685 LoadLogo(&first, 0, FALSE);
\r
3686 LoadLogo(&second, 1, appData.icsActive);
\r
3687 InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos
\r
3688 if(display) DisplayLogos();
\r
3691 static HDC hdcSeek;
\r
3693 // [HGM] seekgraph
\r
3694 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3697 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3698 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3699 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3700 SelectObject( hdcSeek, hp );
\r
3703 // front-end wrapper for drawing functions to do rectangles
\r
3704 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3709 if (hdcSeek == NULL) {
\r
3710 hdcSeek = GetDC(hwndMain);
\r
3711 if (!appData.monoMode) {
\r
3712 SelectPalette(hdcSeek, hPal, FALSE);
\r
3713 RealizePalette(hdcSeek);
\r
3716 hp = SelectObject( hdcSeek, gridPen );
\r
3717 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3718 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3719 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3720 SelectObject( hdcSeek, hp );
\r
3723 // front-end wrapper for putting text in graph
\r
3724 void DrawSeekText(char *buf, int x, int y)
\r
3727 SetBkMode( hdcSeek, TRANSPARENT );
\r
3728 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3729 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3732 void DrawSeekDot(int x, int y, int color)
\r
3734 int square = color & 0x80;
\r
3735 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3736 color == 0 ? markerBrush[1] : color == 1 ? darkSquareBrush : explodeBrush);
\r
3739 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3740 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3742 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3743 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3744 SelectObject(hdcSeek, oldBrush);
\r
3747 void DrawSeekOpen()
\r
3751 void DrawSeekClose()
\r
3756 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3758 static Board lastReq[2], lastDrawn[2];
\r
3759 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3760 static int lastDrawnFlipView = 0;
\r
3761 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3762 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3765 HBITMAP bufferBitmap;
\r
3766 HBITMAP oldBitmap;
\r
3768 HRGN clips[MAX_CLIPS];
\r
3769 ChessSquare dragged_piece = EmptySquare;
\r
3770 int nr = twoBoards*partnerUp;
\r
3772 /* I'm undecided on this - this function figures out whether a full
\r
3773 * repaint is necessary on its own, so there's no real reason to have the
\r
3774 * caller tell it that. I think this can safely be set to FALSE - but
\r
3775 * if we trust the callers not to request full repaints unnessesarily, then
\r
3776 * we could skip some clipping work. In other words, only request a full
\r
3777 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3778 * gamestart and similar) --Hawk
\r
3780 Boolean fullrepaint = repaint;
\r
3782 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3784 if( DrawPositionNeedsFullRepaint() ) {
\r
3785 fullrepaint = TRUE;
\r
3788 if (board == NULL) {
\r
3789 if (!lastReqValid[nr]) {
\r
3792 board = lastReq[nr];
\r
3794 CopyBoard(lastReq[nr], board);
\r
3795 lastReqValid[nr] = 1;
\r
3798 if (doingSizing) {
\r
3802 if (IsIconic(hwndMain)) {
\r
3806 if (hdc == NULL) {
\r
3807 hdc = GetDC(hwndMain);
\r
3808 if (!appData.monoMode) {
\r
3809 SelectPalette(hdc, hPal, FALSE);
\r
3810 RealizePalette(hdc);
\r
3814 releaseDC = FALSE;
\r
3817 /* Create some work-DCs */
\r
3818 hdcmem = CreateCompatibleDC(hdc);
\r
3819 tmphdc = CreateCompatibleDC(hdc);
\r
3821 /* If dragging is in progress, we temporarely remove the piece */
\r
3822 /* [HGM] or temporarily decrease count if stacked */
\r
3823 /* !! Moved to before board compare !! */
\r
3824 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3825 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3826 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3827 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3828 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3830 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3831 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3832 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3834 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3837 /* Figure out which squares need updating by comparing the
\r
3838 * newest board with the last drawn board and checking if
\r
3839 * flipping has changed.
\r
3841 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3842 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3843 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3844 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3845 SquareToPos(row, column, &x, &y);
\r
3846 clips[num_clips++] =
\r
3847 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3851 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3852 for (i=0; i<2; i++) {
\r
3853 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3854 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3855 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3856 lastDrawnHighlight.sq[i].y >= 0) {
\r
3857 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3858 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3859 clips[num_clips++] =
\r
3860 CreateRectRgn(x - lineGap, y - lineGap,
\r
3861 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3863 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3864 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3865 clips[num_clips++] =
\r
3866 CreateRectRgn(x - lineGap, y - lineGap,
\r
3867 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3871 for (i=0; i<2; i++) {
\r
3872 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3873 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3874 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3875 lastDrawnPremove.sq[i].y >= 0) {
\r
3876 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3877 lastDrawnPremove.sq[i].x, &x, &y);
\r
3878 clips[num_clips++] =
\r
3879 CreateRectRgn(x - lineGap, y - lineGap,
\r
3880 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3882 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3883 premoveHighlightInfo.sq[i].y >= 0) {
\r
3884 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3885 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3886 clips[num_clips++] =
\r
3887 CreateRectRgn(x - lineGap, y - lineGap,
\r
3888 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3892 } else { // nr == 1
\r
3893 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3894 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3895 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3896 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3897 for (i=0; i<2; i++) {
\r
3898 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3899 partnerHighlightInfo.sq[i].y >= 0) {
\r
3900 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3901 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3902 clips[num_clips++] =
\r
3903 CreateRectRgn(x - lineGap, y - lineGap,
\r
3904 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3906 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3907 oldPartnerHighlight.sq[i].y >= 0) {
\r
3908 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3909 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3910 clips[num_clips++] =
\r
3911 CreateRectRgn(x - lineGap, y - lineGap,
\r
3912 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3917 fullrepaint = TRUE;
\r
3920 /* Create a buffer bitmap - this is the actual bitmap
\r
3921 * being written to. When all the work is done, we can
\r
3922 * copy it to the real DC (the screen). This avoids
\r
3923 * the problems with flickering.
\r
3925 GetClientRect(hwndMain, &Rect);
\r
3926 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3927 Rect.bottom-Rect.top+1);
\r
3928 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3929 if (!appData.monoMode) {
\r
3930 SelectPalette(hdcmem, hPal, FALSE);
\r
3933 /* Create clips for dragging */
\r
3934 if (!fullrepaint) {
\r
3935 if (dragInfo.from.x >= 0) {
\r
3936 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3937 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3939 if (dragInfo.start.x >= 0) {
\r
3940 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3941 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3943 if (dragInfo.pos.x >= 0) {
\r
3944 x = dragInfo.pos.x - squareSize / 2;
\r
3945 y = dragInfo.pos.y - squareSize / 2;
\r
3946 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3948 if (dragInfo.lastpos.x >= 0) {
\r
3949 x = dragInfo.lastpos.x - squareSize / 2;
\r
3950 y = dragInfo.lastpos.y - squareSize / 2;
\r
3951 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3955 /* Are we animating a move?
\r
3957 * - remove the piece from the board (temporarely)
\r
3958 * - calculate the clipping region
\r
3960 if (!fullrepaint) {
\r
3961 if (animInfo.piece != EmptySquare) {
\r
3962 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3963 x = boardRect.left + animInfo.lastpos.x;
\r
3964 y = boardRect.top + animInfo.lastpos.y;
\r
3965 x2 = boardRect.left + animInfo.pos.x;
\r
3966 y2 = boardRect.top + animInfo.pos.y;
\r
3967 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3968 /* Slight kludge. The real problem is that after AnimateMove is
\r
3969 done, the position on the screen does not match lastDrawn.
\r
3970 This currently causes trouble only on e.p. captures in
\r
3971 atomic, where the piece moves to an empty square and then
\r
3972 explodes. The old and new positions both had an empty square
\r
3973 at the destination, but animation has drawn a piece there and
\r
3974 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3975 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3979 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3980 if (num_clips == 0)
\r
3981 fullrepaint = TRUE;
\r
3983 /* Set clipping on the memory DC */
\r
3984 if (!fullrepaint) {
\r
3985 SelectClipRgn(hdcmem, clips[0]);
\r
3986 for (x = 1; x < num_clips; x++) {
\r
3987 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3988 abort(); // this should never ever happen!
\r
3992 /* Do all the drawing to the memory DC */
\r
3993 if(explodeInfo.radius) { // [HGM] atomic
\r
3995 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3996 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3997 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3998 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3999 x += squareSize/2;
\r
4000 y += squareSize/2;
\r
4001 if(!fullrepaint) {
\r
4002 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
4003 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
4005 DrawGridOnDC(hdcmem);
\r
4006 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
4007 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
4008 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
4009 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
4010 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
4011 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
4012 SelectObject(hdcmem, oldBrush);
\r
4014 if(border) DrawBackgroundOnDC(hdcmem);
\r
4015 DrawGridOnDC(hdcmem);
\r
4016 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
4017 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
4018 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
4020 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
4021 oldPartnerHighlight = partnerHighlightInfo;
\r
4023 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
4025 if(nr == 0) // [HGM] dual: markers only on left board
\r
4026 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
4027 for (column = 0; column < BOARD_WIDTH; column++) {
\r
4028 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
4029 HBRUSH oldBrush = SelectObject(hdcmem, markerBrush[marker[row][column]-1]);
\r
4030 SquareToPos(row, column, &x, &y);
\r
4031 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
4032 x + 3*squareSize/4, y + 3*squareSize/4);
\r
4033 SelectObject(hdcmem, oldBrush);
\r
4038 if( appData.highlightMoveWithArrow ) {
\r
4040 DrawArrowHighlight(hdcmem);
\r
4043 DrawCoordsOnDC(hdcmem);
\r
4045 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
4046 /* to make sure lastDrawn contains what is actually drawn */
\r
4048 /* Put the dragged piece back into place and draw it (out of place!) */
\r
4049 if (dragged_piece != EmptySquare) {
\r
4050 /* [HGM] or restack */
\r
4051 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
4052 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
4054 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
4055 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
4057 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
4058 x = dragInfo.pos.x - squareSize / 2;
\r
4059 y = dragInfo.pos.y - squareSize / 2;
\r
4060 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
4061 ((int) dragInfo.piece < (int) BlackPawn),
\r
4062 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
4065 /* Put the animated piece back into place and draw it */
\r
4066 if (animInfo.piece != EmptySquare) {
\r
4067 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
4068 x = boardRect.left + animInfo.pos.x;
\r
4069 y = boardRect.top + animInfo.pos.y;
\r
4070 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
4071 ((int) animInfo.piece < (int) BlackPawn),
\r
4072 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
4075 /* Release the bufferBitmap by selecting in the old bitmap
\r
4076 * and delete the memory DC
\r
4078 SelectObject(hdcmem, oldBitmap);
\r
4081 /* Set clipping on the target DC */
\r
4082 if (!fullrepaint) {
\r
4083 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
4085 GetRgnBox(clips[x], &rect);
\r
4086 DeleteObject(clips[x]);
\r
4087 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
4088 rect.right + wpMain.width/2, rect.bottom);
\r
4090 SelectClipRgn(hdc, clips[0]);
\r
4091 for (x = 1; x < num_clips; x++) {
\r
4092 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
4093 abort(); // this should never ever happen!
\r
4097 /* Copy the new bitmap onto the screen in one go.
\r
4098 * This way we avoid any flickering
\r
4100 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
4101 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
4102 boardRect.right - boardRect.left,
\r
4103 boardRect.bottom - boardRect.top,
\r
4104 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
4105 if(saveDiagFlag) {
\r
4106 BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData;
\r
4107 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
4109 GetObject(bufferBitmap, sizeof(b), &b);
\r
4110 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\r
4111 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
4112 bih.biWidth = b.bmWidth;
\r
4113 bih.biHeight = b.bmHeight;
\r
4115 bih.biBitCount = b.bmBitsPixel;
\r
4116 bih.biCompression = 0;
\r
4117 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
4118 bih.biXPelsPerMeter = 0;
\r
4119 bih.biYPelsPerMeter = 0;
\r
4120 bih.biClrUsed = 0;
\r
4121 bih.biClrImportant = 0;
\r
4122 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
4123 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
4124 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
4125 // fprintf(diagFile, "%8x\n", (int) pData);
\r
4127 wb = b.bmWidthBytes;
\r
4129 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
4130 int k = ((int*) pData)[i];
\r
4131 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4132 if(j >= 16) break;
\r
4134 if(j >= nrColors) nrColors = j+1;
\r
4136 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
4138 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
4139 for(w=0; w<(wb>>2); w+=2) {
\r
4140 int k = ((int*) pData)[(wb*i>>2) + w];
\r
4141 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4142 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
4143 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
4144 pData[p++] = m | j<<4;
\r
4146 while(p&3) pData[p++] = 0;
\r
4149 wb = ((wb+31)>>5)<<2;
\r
4151 // write BITMAPFILEHEADER
\r
4152 fprintf(diagFile, "BM");
\r
4153 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
4154 fputDW(diagFile, 0);
\r
4155 fputDW(diagFile, 0x36 + (fac?64:0));
\r
4156 // write BITMAPINFOHEADER
\r
4157 fputDW(diagFile, 40);
\r
4158 fputDW(diagFile, b.bmWidth);
\r
4159 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
4160 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
4161 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
4162 fputDW(diagFile, 0);
\r
4163 fputDW(diagFile, 0);
\r
4164 fputDW(diagFile, 0);
\r
4165 fputDW(diagFile, 0);
\r
4166 fputDW(diagFile, 0);
\r
4167 fputDW(diagFile, 0);
\r
4168 // write color table
\r
4170 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
4171 // write bitmap data
\r
4172 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
4173 fputc(pData[i], diagFile);
\r
4178 SelectObject(tmphdc, oldBitmap);
\r
4180 /* Massive cleanup */
\r
4181 for (x = 0; x < num_clips; x++)
\r
4182 DeleteObject(clips[x]);
\r
4185 DeleteObject(bufferBitmap);
\r
4188 ReleaseDC(hwndMain, hdc);
\r
4190 if (lastDrawnFlipView != flipView && nr == 0) {
\r
4192 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
4194 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
4197 /* CopyBoard(lastDrawn, board);*/
\r
4198 lastDrawnHighlight = highlightInfo;
\r
4199 lastDrawnPremove = premoveHighlightInfo;
\r
4200 lastDrawnFlipView = flipView;
\r
4201 lastDrawnValid[nr] = 1;
\r
4204 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
4209 saveDiagFlag = 1; diagFile = f;
\r
4210 HDCDrawPosition(NULL, TRUE, NULL);
\r
4218 /*---------------------------------------------------------------------------*\
\r
4219 | CLIENT PAINT PROCEDURE
\r
4220 | This is the main event-handler for the WM_PAINT message.
\r
4222 \*---------------------------------------------------------------------------*/
\r
4224 PaintProc(HWND hwnd)
\r
4230 if((hdc = BeginPaint(hwnd, &ps))) {
\r
4231 if (IsIconic(hwnd)) {
\r
4232 DrawIcon(hdc, 2, 2, iconCurrent);
\r
4234 if (!appData.monoMode) {
\r
4235 SelectPalette(hdc, hPal, FALSE);
\r
4236 RealizePalette(hdc);
\r
4238 HDCDrawPosition(hdc, 1, NULL);
\r
4239 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
4240 flipView = !flipView; partnerUp = !partnerUp;
\r
4241 HDCDrawPosition(hdc, 1, NULL);
\r
4242 flipView = !flipView; partnerUp = !partnerUp;
\r
4245 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
4246 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
4247 ETO_CLIPPED|ETO_OPAQUE,
\r
4248 &messageRect, messageText, strlen(messageText), NULL);
\r
4249 SelectObject(hdc, oldFont);
\r
4250 DisplayBothClocks();
\r
4253 EndPaint(hwnd,&ps);
\r
4261 * If the user selects on a border boundary, return -1; if off the board,
\r
4262 * return -2. Otherwise map the event coordinate to the square.
\r
4263 * The offset boardRect.left or boardRect.top must already have been
\r
4264 * subtracted from x.
\r
4266 int EventToSquare(x, limit)
\r
4271 if (x < lineGap + border)
\r
4273 x -= lineGap + border;
\r
4274 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4276 x /= (squareSize + lineGap);
\r
4288 DropEnable dropEnables[] = {
\r
4289 { 'P', DP_Pawn, N_("Pawn") },
\r
4290 { 'N', DP_Knight, N_("Knight") },
\r
4291 { 'B', DP_Bishop, N_("Bishop") },
\r
4292 { 'R', DP_Rook, N_("Rook") },
\r
4293 { 'Q', DP_Queen, N_("Queen") },
\r
4297 SetupDropMenu(HMENU hmenu)
\r
4299 int i, count, enable;
\r
4301 extern char white_holding[], black_holding[];
\r
4302 char item[MSG_SIZ];
\r
4304 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4305 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4306 dropEnables[i].piece);
\r
4308 while (p && *p++ == dropEnables[i].piece) count++;
\r
4309 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4310 enable = count > 0 || !appData.testLegality
\r
4311 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4312 && !appData.icsActive);
\r
4313 ModifyMenu(hmenu, dropEnables[i].command,
\r
4314 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4315 dropEnables[i].command, item);
\r
4319 void DragPieceBegin(int x, int y, Boolean instantly)
\r
4321 dragInfo.lastpos.x = boardRect.left + x;
\r
4322 dragInfo.lastpos.y = boardRect.top + y;
\r
4323 if(instantly) dragInfo.pos = dragInfo.lastpos;
\r
4324 dragInfo.from.x = fromX;
\r
4325 dragInfo.from.y = fromY;
\r
4326 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4327 dragInfo.start = dragInfo.from;
\r
4328 SetCapture(hwndMain);
\r
4331 void DragPieceEnd(int x, int y)
\r
4334 dragInfo.start.x = dragInfo.start.y = -1;
\r
4335 dragInfo.from = dragInfo.start;
\r
4336 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4339 void ChangeDragPiece(ChessSquare piece)
\r
4341 dragInfo.piece = piece;
\r
4344 /* Event handler for mouse messages */
\r
4346 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4350 static int recursive = 0;
\r
4352 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4355 if (message == WM_MBUTTONUP) {
\r
4356 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4357 to the middle button: we simulate pressing the left button too!
\r
4359 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4360 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4366 pt.x = LOWORD(lParam);
\r
4367 pt.y = HIWORD(lParam);
\r
4368 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4369 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4370 if (!flipView && y >= 0) {
\r
4371 y = BOARD_HEIGHT - 1 - y;
\r
4373 if (flipView && x >= 0) {
\r
4374 x = BOARD_WIDTH - 1 - x;
\r
4377 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4378 controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status
\r
4380 switch (message) {
\r
4381 case WM_LBUTTONDOWN:
\r
4382 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4383 ClockClick(flipClock); break;
\r
4384 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4385 ClockClick(!flipClock); break;
\r
4387 if(dragging) { // [HGM] lion: don't destroy dragging info if we are already dragging
\r
4388 dragInfo.start.x = dragInfo.start.y = -1;
\r
4389 dragInfo.from = dragInfo.start;
\r
4391 if(fromX == -1 && frozen) { // not sure where this is for
\r
4392 fromX = fromY = -1;
\r
4393 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4396 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4397 DrawPosition(TRUE, NULL);
\r
4400 case WM_LBUTTONUP:
\r
4401 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4402 DrawPosition(TRUE, NULL);
\r
4405 case WM_MOUSEMOVE:
\r
4406 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4407 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4408 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4409 if ((appData.animateDragging || appData.highlightDragging)
\r
4410 && (wParam & MK_LBUTTON || dragging == 2)
\r
4411 && dragInfo.from.x >= 0)
\r
4413 BOOL full_repaint = FALSE;
\r
4415 if (appData.animateDragging) {
\r
4416 dragInfo.pos = pt;
\r
4418 if (appData.highlightDragging) {
\r
4419 HoverEvent(highlightInfo.sq[1].x, highlightInfo.sq[1].y, x, y);
\r
4420 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4421 full_repaint = TRUE;
\r
4425 DrawPosition( full_repaint, NULL);
\r
4427 dragInfo.lastpos = dragInfo.pos;
\r
4431 case WM_MOUSEWHEEL: // [DM]
\r
4432 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4433 /* Mouse Wheel is being rolled forward
\r
4434 * Play moves forward
\r
4436 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4437 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4438 /* Mouse Wheel is being rolled backward
\r
4439 * Play moves backward
\r
4441 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4442 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4446 case WM_MBUTTONUP:
\r
4447 case WM_RBUTTONUP:
\r
4449 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4452 case WM_MBUTTONDOWN:
\r
4453 case WM_RBUTTONDOWN:
\r
4456 fromX = fromY = -1;
\r
4457 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4458 dragInfo.start.x = dragInfo.start.y = -1;
\r
4459 dragInfo.from = dragInfo.start;
\r
4460 dragInfo.lastpos = dragInfo.pos;
\r
4461 if (appData.highlightDragging) {
\r
4462 ClearHighlights();
\r
4465 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4466 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4467 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4468 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4469 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4473 DrawPosition(TRUE, NULL);
\r
4475 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4478 if (message == WM_MBUTTONDOWN) {
\r
4479 buttonCount = 3; /* even if system didn't think so */
\r
4480 if (wParam & MK_SHIFT)
\r
4481 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4483 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4484 } else { /* message == WM_RBUTTONDOWN */
\r
4485 /* Just have one menu, on the right button. Windows users don't
\r
4486 think to try the middle one, and sometimes other software steals
\r
4487 it, or it doesn't really exist. */
\r
4488 if(gameInfo.variant != VariantShogi)
\r
4489 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4491 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4495 SetCapture(hwndMain);
\r
4498 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4499 SetupDropMenu(hmenu);
\r
4500 MenuPopup(hwnd, pt, hmenu, -1);
\r
4510 /* Preprocess messages for buttons in main window */
\r
4512 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4514 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4517 for (i=0; i<N_BUTTONS; i++) {
\r
4518 if (buttonDesc[i].id == id) break;
\r
4520 if (i == N_BUTTONS) return 0;
\r
4521 switch (message) {
\r
4526 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4527 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4534 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4537 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4538 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4539 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4540 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4542 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4544 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4545 TypeInEvent((char)wParam);
\r
4551 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4554 static int promoStyle;
\r
4556 /* Process messages for Promotion dialog box */
\r
4558 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4563 switch (message) {
\r
4565 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4566 /* Center the dialog over the application window */
\r
4567 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4568 Translate(hDlg, DLG_PromotionKing);
\r
4569 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4570 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4571 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4572 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4573 SW_SHOW : SW_HIDE);
\r
4574 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4575 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4576 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4577 PieceToChar(WhiteAngel) != '~') ||
\r
4578 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4579 PieceToChar(BlackAngel) != '~') ) ?
\r
4580 SW_SHOW : SW_HIDE);
\r
4581 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4582 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4583 PieceToChar(WhiteMarshall) != '~') ||
\r
4584 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4585 PieceToChar(BlackMarshall) != '~') ) ?
\r
4586 SW_SHOW : SW_HIDE);
\r
4587 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4588 ShowWindow(GetDlgItem(hDlg, PB_Rook), !promoStyle ? SW_SHOW : SW_HIDE);
\r
4589 ShowWindow(GetDlgItem(hDlg, PB_Bishop), !promoStyle ? SW_SHOW : SW_HIDE);
\r
4591 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4592 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4593 SetWindowText(hDlg, "Promote?");
\r
4595 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4596 gameInfo.variant == VariantSuper ?
\r
4597 SW_SHOW : SW_HIDE);
\r
4600 case WM_COMMAND: /* message: received a command */
\r
4601 switch (LOWORD(wParam)) {
\r
4603 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4604 ClearHighlights();
\r
4605 DrawPosition(FALSE, NULL);
\r
4608 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4611 promoChar = promoStyle ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4614 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4615 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4618 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4619 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4621 case PB_Chancellor:
\r
4622 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4624 case PB_Archbishop:
\r
4625 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4628 promoChar = gameInfo.variant == VariantShogi ? '=' : promoStyle ? NULLCHAR :
\r
4629 ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight));
\r
4634 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4635 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4636 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4637 fromX = fromY = -1;
\r
4638 if (!appData.highlightLastMove) {
\r
4639 ClearHighlights();
\r
4640 DrawPosition(FALSE, NULL);
\r
4647 /* Pop up promotion dialog */
\r
4649 PromotionPopup(HWND hwnd)
\r
4653 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4654 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4655 hwnd, (DLGPROC)lpProc);
\r
4656 FreeProcInstance(lpProc);
\r
4660 PromotionPopUp(char choice)
\r
4662 promoStyle = (choice == '+' || IS_SHOGI(gameInfo.variant));
\r
4663 DrawPosition(TRUE, NULL);
\r
4664 PromotionPopup(hwndMain);
\r
4668 LoadGameDialog(HWND hwnd, char* title)
\r
4672 char fileTitle[MSG_SIZ];
\r
4673 f = OpenFileDialog(hwnd, "rb", "",
\r
4674 appData.oldSaveStyle ? "gam" : "pgn",
\r
4676 title, &number, fileTitle, NULL);
\r
4678 cmailMsgLoaded = FALSE;
\r
4679 if (number == 0) {
\r
4680 int error = GameListBuild(f);
\r
4682 DisplayError(_("Cannot build game list"), error);
\r
4683 } else if (!ListEmpty(&gameList) &&
\r
4684 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4685 GameListPopUp(f, fileTitle);
\r
4688 GameListDestroy();
\r
4691 LoadGame(f, number, fileTitle, FALSE);
\r
4695 int get_term_width()
\r
4700 HFONT hfont, hold_font;
\r
4705 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4709 // get the text metrics
\r
4710 hdc = GetDC(hText);
\r
4711 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4712 if (consoleCF.dwEffects & CFE_BOLD)
\r
4713 lf.lfWeight = FW_BOLD;
\r
4714 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4715 lf.lfItalic = TRUE;
\r
4716 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4717 lf.lfStrikeOut = TRUE;
\r
4718 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4719 lf.lfUnderline = TRUE;
\r
4720 hfont = CreateFontIndirect(&lf);
\r
4721 hold_font = SelectObject(hdc, hfont);
\r
4722 GetTextMetrics(hdc, &tm);
\r
4723 SelectObject(hdc, hold_font);
\r
4724 DeleteObject(hfont);
\r
4725 ReleaseDC(hText, hdc);
\r
4727 // get the rectangle
\r
4728 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4730 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4733 void UpdateICSWidth(HWND hText)
\r
4735 LONG old_width, new_width;
\r
4737 new_width = get_term_width(hText, FALSE);
\r
4738 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4739 if (new_width != old_width)
\r
4741 ics_update_width(new_width);
\r
4742 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4747 ChangedConsoleFont()
\r
4750 CHARRANGE tmpsel, sel;
\r
4751 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4752 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4753 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4756 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4757 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4758 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4759 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4760 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4761 * size. This was undocumented in the version of MSVC++ that I had
\r
4762 * when I wrote the code, but is apparently documented now.
\r
4764 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4765 cfmt.bCharSet = f->lf.lfCharSet;
\r
4766 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4767 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4768 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4769 /* Why are the following seemingly needed too? */
\r
4770 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4771 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4772 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4774 tmpsel.cpMax = -1; /*999999?*/
\r
4775 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4776 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4777 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4778 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4780 paraf.cbSize = sizeof(paraf);
\r
4781 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4782 paraf.dxStartIndent = 0;
\r
4783 paraf.dxOffset = WRAP_INDENT;
\r
4784 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4785 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4786 UpdateICSWidth(hText);
\r
4789 /*---------------------------------------------------------------------------*\
\r
4791 * Window Proc for main window
\r
4793 \*---------------------------------------------------------------------------*/
\r
4795 /* Process messages for main window, etc. */
\r
4797 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4804 char fileTitle[MSG_SIZ];
\r
4805 static SnapData sd;
\r
4806 static int peek=0;
\r
4808 switch (message) {
\r
4810 case WM_PAINT: /* message: repaint portion of window */
\r
4814 case WM_ERASEBKGND:
\r
4815 if (IsIconic(hwnd)) {
\r
4816 /* Cheat; change the message */
\r
4817 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4819 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4823 case WM_LBUTTONDOWN:
\r
4824 case WM_MBUTTONDOWN:
\r
4825 case WM_RBUTTONDOWN:
\r
4826 case WM_LBUTTONUP:
\r
4827 case WM_MBUTTONUP:
\r
4828 case WM_RBUTTONUP:
\r
4829 case WM_MOUSEMOVE:
\r
4830 case WM_MOUSEWHEEL:
\r
4831 MouseEvent(hwnd, message, wParam, lParam);
\r
4835 if((char)wParam == '\b') {
\r
4836 ForwardEvent(); peek = 0;
\r
4839 JAWS_KBUP_NAVIGATION
\r
4844 if((char)wParam == '\b') {
\r
4845 if(!peek) BackwardEvent(), peek = 1;
\r
4848 JAWS_KBDOWN_NAVIGATION
\r
4854 JAWS_ALT_INTERCEPT
\r
4856 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4857 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4858 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4859 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4861 SendMessage(h, message, wParam, lParam);
\r
4862 } else if(lParam != KF_REPEAT) {
\r
4863 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4864 TypeInEvent((char)wParam);
\r
4865 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4866 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4871 case WM_PALETTECHANGED:
\r
4872 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4874 HDC hdc = GetDC(hwndMain);
\r
4875 SelectPalette(hdc, hPal, TRUE);
\r
4876 nnew = RealizePalette(hdc);
\r
4878 paletteChanged = TRUE;
\r
4880 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4882 ReleaseDC(hwnd, hdc);
\r
4886 case WM_QUERYNEWPALETTE:
\r
4887 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4889 HDC hdc = GetDC(hwndMain);
\r
4890 paletteChanged = FALSE;
\r
4891 SelectPalette(hdc, hPal, FALSE);
\r
4892 nnew = RealizePalette(hdc);
\r
4894 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4896 ReleaseDC(hwnd, hdc);
\r
4901 case WM_COMMAND: /* message: command from application menu */
\r
4902 wmId = LOWORD(wParam);
\r
4907 SAY("new game enter a move to play against the computer with white");
\r
4910 case IDM_NewGameFRC:
\r
4911 if( NewGameFRC() == 0 ) {
\r
4916 case IDM_NewVariant:
\r
4917 NewVariantPopup(hwnd);
\r
4920 case IDM_LoadGame:
\r
4921 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4924 case IDM_LoadNextGame:
\r
4928 case IDM_LoadPrevGame:
\r
4932 case IDM_ReloadGame:
\r
4936 case IDM_LoadPosition:
\r
4937 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4938 Reset(FALSE, TRUE);
\r
4941 f = OpenFileDialog(hwnd, "rb", "",
\r
4942 appData.oldSaveStyle ? "pos" : "fen",
\r
4944 _("Load Position from File"), &number, fileTitle, NULL);
\r
4946 LoadPosition(f, number, fileTitle);
\r
4950 case IDM_LoadNextPosition:
\r
4951 ReloadPosition(1);
\r
4954 case IDM_LoadPrevPosition:
\r
4955 ReloadPosition(-1);
\r
4958 case IDM_ReloadPosition:
\r
4959 ReloadPosition(0);
\r
4962 case IDM_SaveGame:
\r
4963 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4964 f = OpenFileDialog(hwnd, "a", defName,
\r
4965 appData.oldSaveStyle ? "gam" : "pgn",
\r
4967 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4969 SaveGame(f, 0, "");
\r
4973 case IDM_SavePosition:
\r
4974 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4975 f = OpenFileDialog(hwnd, "a", defName,
\r
4976 appData.oldSaveStyle ? "pos" : "fen",
\r
4978 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4980 SavePosition(f, 0, "");
\r
4984 case IDM_SaveDiagram:
\r
4985 defName = "diagram";
\r
4986 f = OpenFileDialog(hwnd, "wb", defName,
\r
4989 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
4995 case IDM_SaveSelected:
\r
4996 f = OpenFileDialog(hwnd, "a", "",
\r
4999 _("Save Game to File"), NULL, fileTitle, NULL);
\r
5001 SaveSelected(f, 0, "");
\r
5005 case IDM_CreateBook:
\r
5006 CreateBookEvent();
\r
5009 case IDM_CopyGame:
\r
5010 CopyGameToClipboard();
\r
5013 case IDM_PasteGame:
\r
5014 PasteGameFromClipboard();
\r
5017 case IDM_CopyGameListToClipboard:
\r
5018 CopyGameListToClipboard();
\r
5021 /* [AS] Autodetect FEN or PGN data */
\r
5022 case IDM_PasteAny:
\r
5023 PasteGameOrFENFromClipboard();
\r
5026 /* [AS] Move history */
\r
5027 case IDM_ShowMoveHistory:
\r
5028 if( MoveHistoryIsUp() ) {
\r
5029 MoveHistoryPopDown();
\r
5032 MoveHistoryPopUp();
\r
5036 /* [AS] Eval graph */
\r
5037 case IDM_ShowEvalGraph:
\r
5038 if( EvalGraphIsUp() ) {
\r
5039 EvalGraphPopDown();
\r
5043 SetFocus(hwndMain);
\r
5047 /* [AS] Engine output */
\r
5048 case IDM_ShowEngineOutput:
\r
5049 if( EngineOutputIsUp() ) {
\r
5050 EngineOutputPopDown();
\r
5053 EngineOutputPopUp();
\r
5057 /* [AS] User adjudication */
\r
5058 case IDM_UserAdjudication_White:
\r
5059 UserAdjudicationEvent( +1 );
\r
5062 case IDM_UserAdjudication_Black:
\r
5063 UserAdjudicationEvent( -1 );
\r
5066 case IDM_UserAdjudication_Draw:
\r
5067 UserAdjudicationEvent( 0 );
\r
5070 /* [AS] Game list options dialog */
\r
5071 case IDM_GameListOptions:
\r
5072 GameListOptions();
\r
5079 case IDM_CopyPosition:
\r
5080 CopyFENToClipboard();
\r
5083 case IDM_PastePosition:
\r
5084 PasteFENFromClipboard();
\r
5087 case IDM_MailMove:
\r
5091 case IDM_ReloadCMailMsg:
\r
5092 Reset(TRUE, TRUE);
\r
5093 ReloadCmailMsgEvent(FALSE);
\r
5096 case IDM_Minimize:
\r
5097 ShowWindow(hwnd, SW_MINIMIZE);
\r
5104 case IDM_MachineWhite:
\r
5105 MachineWhiteEvent();
\r
5107 * refresh the tags dialog only if it's visible
\r
5109 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
5111 tags = PGNTags(&gameInfo);
\r
5112 TagsPopUp(tags, CmailMsg());
\r
5115 SAY("computer starts playing white");
\r
5118 case IDM_MachineBlack:
\r
5119 MachineBlackEvent();
\r
5121 * refresh the tags dialog only if it's visible
\r
5123 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
5125 tags = PGNTags(&gameInfo);
\r
5126 TagsPopUp(tags, CmailMsg());
\r
5129 SAY("computer starts playing black");
\r
5132 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
5133 if(matchMode) EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_GRAYED);
\r
5134 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
5137 case IDM_TwoMachines:
\r
5138 TwoMachinesEvent();
\r
5141 * refresh the tags dialog only if it's visible
\r
5143 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
5145 tags = PGNTags(&gameInfo);
\r
5146 TagsPopUp(tags, CmailMsg());
\r
5149 SAY("computer starts playing both sides");
\r
5152 case IDM_AnalysisMode:
\r
5153 if(AnalyzeModeEvent()) {
\r
5154 SAY("analyzing current position");
\r
5158 case IDM_AnalyzeFile:
\r
5159 AnalyzeFileEvent();
\r
5162 case IDM_IcsClient:
\r
5166 case IDM_EditGame:
\r
5167 case IDM_EditGame2:
\r
5172 case IDM_EditPosition:
\r
5173 case IDM_EditPosition2:
\r
5174 EditPositionEvent();
\r
5175 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
5178 case IDM_Training:
\r
5182 case IDM_ShowGameList:
\r
5183 ShowGameListProc();
\r
5186 case IDM_EditProgs1:
\r
5187 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
5190 case IDM_LoadProg1:
\r
5191 LoadEnginePopUp(hwndMain, 0);
\r
5194 case IDM_LoadProg2:
\r
5195 LoadEnginePopUp(hwndMain, 1);
\r
5198 case IDM_EditServers:
\r
5199 EditTagsPopUp(icsNames, &icsNames);
\r
5202 case IDM_EditTags:
\r
5207 case IDM_EditBook:
\r
5211 case IDM_EditComment:
\r
5213 if (commentUp && editComment) {
\r
5216 EditCommentEvent();
\r
5237 case IDM_CallFlag:
\r
5257 case IDM_StopObserving:
\r
5258 StopObservingEvent();
\r
5261 case IDM_StopExamining:
\r
5262 StopExaminingEvent();
\r
5266 UploadGameEvent();
\r
5269 case IDM_TypeInMove:
\r
5270 TypeInEvent('\000');
\r
5273 case IDM_TypeInName:
\r
5274 PopUpNameDialog('\000');
\r
5277 case IDM_Backward:
\r
5279 SetFocus(hwndMain);
\r
5286 SetFocus(hwndMain);
\r
5291 SetFocus(hwndMain);
\r
5296 SetFocus(hwndMain);
\r
5299 case OPT_GameListNext: // [HGM] forward these two accelerators to Game List
\r
5300 case OPT_GameListPrev:
\r
5301 if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);
\r
5305 RevertEvent(FALSE);
\r
5308 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5309 RevertEvent(TRUE);
\r
5312 case IDM_TruncateGame:
\r
5313 TruncateGameEvent();
\r
5320 case IDM_RetractMove:
\r
5321 RetractMoveEvent();
\r
5324 case IDM_FlipView:
\r
5325 flipView = !flipView;
\r
5326 DrawPosition(FALSE, NULL);
\r
5329 case IDM_FlipClock:
\r
5330 flipClock = !flipClock;
\r
5331 DisplayBothClocks();
\r
5335 case IDM_MuteSounds:
\r
5336 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5337 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5338 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5341 case IDM_GeneralOptions:
\r
5342 GeneralOptionsPopup(hwnd);
\r
5343 DrawPosition(TRUE, NULL);
\r
5346 case IDM_BoardOptions:
\r
5347 BoardOptionsPopup(hwnd);
\r
5350 case IDM_ThemeOptions:
\r
5351 ThemeOptionsPopup(hwnd);
\r
5354 case IDM_EnginePlayOptions:
\r
5355 EnginePlayOptionsPopup(hwnd);
\r
5358 case IDM_Engine1Options:
\r
5359 EngineOptionsPopup(hwnd, &first);
\r
5362 case IDM_Engine2Options:
\r
5364 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5365 EngineOptionsPopup(hwnd, &second);
\r
5368 case IDM_OptionsUCI:
\r
5369 UciOptionsPopup(hwnd);
\r
5373 TourneyPopup(hwnd);
\r
5376 case IDM_IcsOptions:
\r
5377 IcsOptionsPopup(hwnd);
\r
5381 FontsOptionsPopup(hwnd);
\r
5385 SoundOptionsPopup(hwnd);
\r
5388 case IDM_CommPort:
\r
5389 CommPortOptionsPopup(hwnd);
\r
5392 case IDM_LoadOptions:
\r
5393 LoadOptionsPopup(hwnd);
\r
5396 case IDM_SaveOptions:
\r
5397 SaveOptionsPopup(hwnd);
\r
5400 case IDM_TimeControl:
\r
5401 TimeControlOptionsPopup(hwnd);
\r
5404 case IDM_SaveSettings:
\r
5405 SaveSettings(settingsFileName);
\r
5408 case IDM_SaveSettingsOnExit:
\r
5409 saveSettingsOnExit = !saveSettingsOnExit;
\r
5410 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5411 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5412 MF_CHECKED : MF_UNCHECKED));
\r
5423 case IDM_AboutGame:
\r
5428 appData.debugMode = !appData.debugMode;
\r
5429 if (appData.debugMode) {
\r
5430 char dir[MSG_SIZ];
\r
5431 GetCurrentDirectory(MSG_SIZ, dir);
\r
5432 SetCurrentDirectory(installDir);
\r
5433 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5434 SetCurrentDirectory(dir);
\r
5435 setbuf(debugFP, NULL);
\r
5442 case IDM_HELPCONTENTS:
\r
5443 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5444 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5445 MessageBox (GetFocus(),
\r
5446 _("Unable to activate help"),
\r
5447 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5451 case IDM_HELPSEARCH:
\r
5452 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5453 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5454 MessageBox (GetFocus(),
\r
5455 _("Unable to activate help"),
\r
5456 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5460 case IDM_HELPHELP:
\r
5461 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5462 MessageBox (GetFocus(),
\r
5463 _("Unable to activate help"),
\r
5464 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5469 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5471 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5472 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5473 FreeProcInstance(lpProc);
\r
5476 case IDM_DirectCommand1:
\r
5477 AskQuestionEvent(_("Direct Command"),
\r
5478 _("Send to chess program:"), "", "1");
\r
5480 case IDM_DirectCommand2:
\r
5481 AskQuestionEvent(_("Direct Command"),
\r
5482 _("Send to second chess program:"), "", "2");
\r
5485 case EP_WhitePawn:
\r
5486 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5487 fromX = fromY = -1;
\r
5490 case EP_WhiteKnight:
\r
5491 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5492 fromX = fromY = -1;
\r
5495 case EP_WhiteBishop:
\r
5496 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5497 fromX = fromY = -1;
\r
5500 case EP_WhiteRook:
\r
5501 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5502 fromX = fromY = -1;
\r
5505 case EP_WhiteQueen:
\r
5506 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5507 fromX = fromY = -1;
\r
5510 case EP_WhiteFerz:
\r
5511 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5512 fromX = fromY = -1;
\r
5515 case EP_WhiteWazir:
\r
5516 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5517 fromX = fromY = -1;
\r
5520 case EP_WhiteAlfil:
\r
5521 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5522 fromX = fromY = -1;
\r
5525 case EP_WhiteCannon:
\r
5526 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5527 fromX = fromY = -1;
\r
5530 case EP_WhiteCardinal:
\r
5531 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5532 fromX = fromY = -1;
\r
5535 case EP_WhiteMarshall:
\r
5536 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5537 fromX = fromY = -1;
\r
5540 case EP_WhiteKing:
\r
5541 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5542 fromX = fromY = -1;
\r
5545 case EP_BlackPawn:
\r
5546 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5547 fromX = fromY = -1;
\r
5550 case EP_BlackKnight:
\r
5551 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5552 fromX = fromY = -1;
\r
5555 case EP_BlackBishop:
\r
5556 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5557 fromX = fromY = -1;
\r
5560 case EP_BlackRook:
\r
5561 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5562 fromX = fromY = -1;
\r
5565 case EP_BlackQueen:
\r
5566 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5567 fromX = fromY = -1;
\r
5570 case EP_BlackFerz:
\r
5571 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5572 fromX = fromY = -1;
\r
5575 case EP_BlackWazir:
\r
5576 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5577 fromX = fromY = -1;
\r
5580 case EP_BlackAlfil:
\r
5581 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5582 fromX = fromY = -1;
\r
5585 case EP_BlackCannon:
\r
5586 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5587 fromX = fromY = -1;
\r
5590 case EP_BlackCardinal:
\r
5591 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5592 fromX = fromY = -1;
\r
5595 case EP_BlackMarshall:
\r
5596 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5597 fromX = fromY = -1;
\r
5600 case EP_BlackKing:
\r
5601 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5602 fromX = fromY = -1;
\r
5605 case EP_EmptySquare:
\r
5606 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5607 fromX = fromY = -1;
\r
5610 case EP_ClearBoard:
\r
5611 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5612 fromX = fromY = -1;
\r
5616 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5617 fromX = fromY = -1;
\r
5621 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5622 fromX = fromY = -1;
\r
5626 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5627 fromX = fromY = -1;
\r
5631 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5632 fromX = fromY = -1;
\r
5636 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5637 fromX = fromY = -1;
\r
5641 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5642 fromX = fromY = -1;
\r
5646 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5647 fromX = fromY = -1;
\r
5651 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5652 fromX = fromY = -1;
\r
5656 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5657 fromX = fromY = -1;
\r
5661 barbaric = 0; appData.language = "";
\r
5662 TranslateMenus(0);
\r
5663 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5664 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5665 lastChecked = wmId;
\r
5669 if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)
\r
5670 RecentEngineEvent(wmId - IDM_RecentEngines);
\r
5672 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5673 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5674 TranslateMenus(0);
\r
5675 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5676 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5677 lastChecked = wmId;
\r
5680 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5686 case CLOCK_TIMER_ID:
\r
5687 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5688 clockTimerEvent = 0;
\r
5689 DecrementClocks(); /* call into back end */
\r
5691 case LOAD_GAME_TIMER_ID:
\r
5692 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5693 loadGameTimerEvent = 0;
\r
5694 AutoPlayGameLoop(); /* call into back end */
\r
5696 case ANALYSIS_TIMER_ID:
\r
5697 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5698 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5699 AnalysisPeriodicEvent(0);
\r
5701 KillTimer(hwnd, analysisTimerEvent);
\r
5702 analysisTimerEvent = 0;
\r
5705 case DELAYED_TIMER_ID:
\r
5706 KillTimer(hwnd, delayedTimerEvent);
\r
5707 delayedTimerEvent = 0;
\r
5708 delayedTimerCallback();
\r
5713 case WM_USER_Input:
\r
5714 InputEvent(hwnd, message, wParam, lParam);
\r
5717 /* [AS] Also move "attached" child windows */
\r
5718 case WM_WINDOWPOSCHANGING:
\r
5720 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5721 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5723 if( ((lpwp->flags & SWP_NOMOVE) == 0) /*&& ((lpwp->flags & SWP_NOSIZE) != 0)*/ ) { // [HGM] in Win8 size always accompanies move?
\r
5724 /* Window is moving */
\r
5727 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5728 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5729 rcMain.right = wpMain.x + wpMain.width;
\r
5730 rcMain.top = wpMain.y;
\r
5731 rcMain.bottom = wpMain.y + wpMain.height;
\r
5733 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5734 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5735 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5736 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5737 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5738 wpMain.x = lpwp->x;
\r
5739 wpMain.y = lpwp->y;
\r
5745 /* [AS] Snapping */
\r
5746 case WM_ENTERSIZEMOVE:
\r
5747 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5748 if (hwnd == hwndMain) {
\r
5749 doingSizing = TRUE;
\r
5752 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5756 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5757 if (hwnd == hwndMain) {
\r
5758 lastSizing = wParam;
\r
5763 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5764 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5766 case WM_EXITSIZEMOVE:
\r
5767 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5768 if (hwnd == hwndMain) {
\r
5770 doingSizing = FALSE;
\r
5771 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5772 GetClientRect(hwnd, &client);
\r
5773 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5775 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5777 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5780 case WM_DESTROY: /* message: window being destroyed */
\r
5781 PostQuitMessage(0);
\r
5785 if (hwnd == hwndMain) {
\r
5790 default: /* Passes it on if unprocessed */
\r
5791 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5798 /*---------------------------------------------------------------------------*\
\r
5800 * Misc utility routines
\r
5802 \*---------------------------------------------------------------------------*/
\r
5805 * Decent random number generator, at least not as bad as Windows
\r
5806 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5808 unsigned int randstate;
\r
5813 randstate = randstate * 1664525 + 1013904223;
\r
5814 return (int) randstate & 0x7fffffff;
\r
5818 mysrandom(unsigned int seed)
\r
5825 * returns TRUE if user selects a different color, FALSE otherwise
\r
5829 ChangeColor(HWND hwnd, COLORREF *which)
\r
5831 static BOOL firstTime = TRUE;
\r
5832 static DWORD customColors[16];
\r
5834 COLORREF newcolor;
\r
5839 /* Make initial colors in use available as custom colors */
\r
5840 /* Should we put the compiled-in defaults here instead? */
\r
5842 customColors[i++] = lightSquareColor & 0xffffff;
\r
5843 customColors[i++] = darkSquareColor & 0xffffff;
\r
5844 customColors[i++] = whitePieceColor & 0xffffff;
\r
5845 customColors[i++] = blackPieceColor & 0xffffff;
\r
5846 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5847 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5849 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5850 customColors[i++] = textAttribs[ccl].color;
\r
5852 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5853 firstTime = FALSE;
\r
5856 cc.lStructSize = sizeof(cc);
\r
5857 cc.hwndOwner = hwnd;
\r
5858 cc.hInstance = NULL;
\r
5859 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5860 cc.lpCustColors = (LPDWORD) customColors;
\r
5861 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5863 if (!ChooseColor(&cc)) return FALSE;
\r
5865 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5866 if (newcolor == *which) return FALSE;
\r
5867 *which = newcolor;
\r
5871 InitDrawingColors();
\r
5872 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5877 MyLoadSound(MySound *ms)
\r
5883 if (ms->data && ms->flag) free(ms->data);
\r
5886 switch (ms->name[0]) {
\r
5892 /* System sound from Control Panel. Don't preload here. */
\r
5896 if (ms->name[1] == NULLCHAR) {
\r
5897 /* "!" alone = silence */
\r
5900 /* Builtin wave resource. Error if not found. */
\r
5901 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5902 if (h == NULL) break;
\r
5903 ms->data = (void *)LoadResource(hInst, h);
\r
5904 ms->flag = 0; // not maloced, so cannot be freed!
\r
5905 if (h == NULL) break;
\r
5910 /* .wav file. Error if not found. */
\r
5911 f = fopen(ms->name, "rb");
\r
5912 if (f == NULL) break;
\r
5913 if (fstat(fileno(f), &st) < 0) break;
\r
5914 ms->data = malloc(st.st_size);
\r
5916 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5922 char buf[MSG_SIZ];
\r
5923 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5924 DisplayError(buf, GetLastError());
\r
5930 MyPlaySound(MySound *ms)
\r
5932 BOOLEAN ok = FALSE;
\r
5934 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5935 switch (ms->name[0]) {
\r
5937 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5942 /* System sound from Control Panel (deprecated feature).
\r
5943 "$" alone or an unset sound name gets default beep (still in use). */
\r
5944 if (ms->name[1]) {
\r
5945 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5947 if (!ok) ok = MessageBeep(MB_OK);
\r
5950 /* Builtin wave resource, or "!" alone for silence */
\r
5951 if (ms->name[1]) {
\r
5952 if (ms->data == NULL) return FALSE;
\r
5953 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5959 /* .wav file. Error if not found. */
\r
5960 if (ms->data == NULL) return FALSE;
\r
5961 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5964 /* Don't print an error: this can happen innocently if the sound driver
\r
5965 is busy; for instance, if another instance of WinBoard is playing
\r
5966 a sound at about the same time. */
\r
5972 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5975 OPENFILENAME *ofn;
\r
5976 static UINT *number; /* gross that this is static */
\r
5978 switch (message) {
\r
5979 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5980 /* Center the dialog over the application window */
\r
5981 ofn = (OPENFILENAME *) lParam;
\r
5982 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5983 number = (UINT *) ofn->lCustData;
\r
5984 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5988 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5989 Translate(hDlg, 1536);
\r
5990 return FALSE; /* Allow for further processing */
\r
5993 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5994 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5996 return FALSE; /* Allow for further processing */
\r
6002 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
6004 static UINT *number;
\r
6005 OPENFILENAME *ofname;
\r
6008 case WM_INITDIALOG:
\r
6009 Translate(hdlg, DLG_IndexNumber);
\r
6010 ofname = (OPENFILENAME *)lParam;
\r
6011 number = (UINT *)(ofname->lCustData);
\r
6014 ofnot = (OFNOTIFY *)lParam;
\r
6015 if (ofnot->hdr.code == CDN_FILEOK) {
\r
6016 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
6025 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
6026 char *nameFilt, char *dlgTitle, UINT *number,
\r
6027 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
6029 OPENFILENAME openFileName;
\r
6030 char buf1[MSG_SIZ];
\r
6033 if (fileName == NULL) fileName = buf1;
\r
6034 if (defName == NULL) {
\r
6035 safeStrCpy(fileName, "*.", 3 );
\r
6036 strcat(fileName, defExt);
\r
6038 safeStrCpy(fileName, defName, MSG_SIZ );
\r
6040 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
6041 if (number) *number = 0;
\r
6043 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
6044 openFileName.hwndOwner = hwnd;
\r
6045 openFileName.hInstance = (HANDLE) hInst;
\r
6046 openFileName.lpstrFilter = nameFilt;
\r
6047 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
6048 openFileName.nMaxCustFilter = 0L;
\r
6049 openFileName.nFilterIndex = 1L;
\r
6050 openFileName.lpstrFile = fileName;
\r
6051 openFileName.nMaxFile = MSG_SIZ;
\r
6052 openFileName.lpstrFileTitle = fileTitle;
\r
6053 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
6054 openFileName.lpstrInitialDir = NULL;
\r
6055 openFileName.lpstrTitle = dlgTitle;
\r
6056 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
6057 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
6058 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
6059 | (oldDialog ? 0 : OFN_EXPLORER);
\r
6060 openFileName.nFileOffset = 0;
\r
6061 openFileName.nFileExtension = 0;
\r
6062 openFileName.lpstrDefExt = defExt;
\r
6063 openFileName.lCustData = (LONG) number;
\r
6064 openFileName.lpfnHook = oldDialog ?
\r
6065 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
6066 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
6068 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
6069 GetOpenFileName(&openFileName)) {
\r
6070 /* open the file */
\r
6071 f = fopen(openFileName.lpstrFile, write);
\r
6073 MessageBox(hwnd, _("File open failed"), NULL,
\r
6074 MB_OK|MB_ICONEXCLAMATION);
\r
6078 int err = CommDlgExtendedError();
\r
6079 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
6088 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
6090 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
6093 * Get the first pop-up menu in the menu template. This is the
\r
6094 * menu that TrackPopupMenu displays.
\r
6096 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
6097 TranslateOneMenu(10, hmenuTrackPopup);
\r
6099 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
6102 * TrackPopup uses screen coordinates, so convert the
\r
6103 * coordinates of the mouse click to screen coordinates.
\r
6105 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
6107 /* Draw and track the floating pop-up menu. */
\r
6108 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
6109 pt.x, pt.y, 0, hwnd, NULL);
\r
6111 /* Destroy the menu.*/
\r
6112 DestroyMenu(hmenu);
\r
6117 int sizeX, sizeY, newSizeX, newSizeY;
\r
6119 } ResizeEditPlusButtonsClosure;
\r
6122 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
6124 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
6128 if (hChild == cl->hText) return TRUE;
\r
6129 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
6130 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
6131 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
6132 ScreenToClient(cl->hDlg, &pt);
\r
6133 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
6134 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
6138 /* Resize a dialog that has a (rich) edit field filling most of
\r
6139 the top, with a row of buttons below */
\r
6141 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
6144 int newTextHeight, newTextWidth;
\r
6145 ResizeEditPlusButtonsClosure cl;
\r
6147 /*if (IsIconic(hDlg)) return;*/
\r
6148 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
6150 cl.hdwp = BeginDeferWindowPos(8);
\r
6152 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
6153 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6154 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6155 if (newTextHeight < 0) {
\r
6156 newSizeY += -newTextHeight;
\r
6157 newTextHeight = 0;
\r
6159 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
6160 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6166 cl.newSizeX = newSizeX;
\r
6167 cl.newSizeY = newSizeY;
\r
6168 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
6170 EndDeferWindowPos(cl.hdwp);
\r
6173 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
6175 RECT rChild, rParent;
\r
6176 int wChild, hChild, wParent, hParent;
\r
6177 int wScreen, hScreen, xNew, yNew;
\r
6180 /* Get the Height and Width of the child window */
\r
6181 GetWindowRect (hwndChild, &rChild);
\r
6182 wChild = rChild.right - rChild.left;
\r
6183 hChild = rChild.bottom - rChild.top;
\r
6185 /* Get the Height and Width of the parent window */
\r
6186 GetWindowRect (hwndParent, &rParent);
\r
6187 wParent = rParent.right - rParent.left;
\r
6188 hParent = rParent.bottom - rParent.top;
\r
6190 /* Get the display limits */
\r
6191 hdc = GetDC (hwndChild);
\r
6192 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
6193 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
6194 ReleaseDC(hwndChild, hdc);
\r
6196 /* Calculate new X position, then adjust for screen */
\r
6197 xNew = rParent.left + ((wParent - wChild) /2);
\r
6200 } else if ((xNew+wChild) > wScreen) {
\r
6201 xNew = wScreen - wChild;
\r
6204 /* Calculate new Y position, then adjust for screen */
\r
6206 yNew = rParent.top + ((hParent - hChild) /2);
\r
6209 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
6214 } else if ((yNew+hChild) > hScreen) {
\r
6215 yNew = hScreen - hChild;
\r
6218 /* Set it, and return */
\r
6219 return SetWindowPos (hwndChild, NULL,
\r
6220 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
6223 /* Center one window over another */
\r
6224 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
6226 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
6229 /*---------------------------------------------------------------------------*\
\r
6231 * Startup Dialog functions
\r
6233 \*---------------------------------------------------------------------------*/
\r
6235 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
6237 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6239 while (*cd != NULL) {
\r
6240 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
6246 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
6248 char buf1[MAX_ARG_LEN];
\r
6251 if (str[0] == '@') {
\r
6252 FILE* f = fopen(str + 1, "r");
\r
6254 DisplayFatalError(str + 1, errno, 2);
\r
6257 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
6259 buf1[len] = NULLCHAR;
\r
6263 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6266 char buf[MSG_SIZ];
\r
6267 char *end = strchr(str, '\n');
\r
6268 if (end == NULL) return;
\r
6269 memcpy(buf, str, end - str);
\r
6270 buf[end - str] = NULLCHAR;
\r
6271 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6277 SetStartupDialogEnables(HWND hDlg)
\r
6279 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6280 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6281 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6282 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6283 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6284 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6285 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6286 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6287 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6288 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6289 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6290 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6291 IsDlgButtonChecked(hDlg, OPT_View));
\r
6295 QuoteForFilename(char *filename)
\r
6297 int dquote, space;
\r
6298 dquote = strchr(filename, '"') != NULL;
\r
6299 space = strchr(filename, ' ') != NULL;
\r
6300 if (dquote || space) {
\r
6312 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6314 char buf[MSG_SIZ];
\r
6317 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6318 q = QuoteForFilename(nthcp);
\r
6319 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6320 if (*nthdir != NULLCHAR) {
\r
6321 q = QuoteForFilename(nthdir);
\r
6322 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6324 if (*nthcp == NULLCHAR) {
\r
6325 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6326 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6327 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6328 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6333 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6335 char buf[MSG_SIZ];
\r
6339 switch (message) {
\r
6340 case WM_INITDIALOG:
\r
6341 /* Center the dialog */
\r
6342 CenterWindow (hDlg, GetDesktopWindow());
\r
6343 Translate(hDlg, DLG_Startup);
\r
6344 /* Initialize the dialog items */
\r
6345 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6346 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6347 firstChessProgramNames);
\r
6348 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6349 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6350 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6351 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6352 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6353 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6354 if (*appData.icsHelper != NULLCHAR) {
\r
6355 char *q = QuoteForFilename(appData.icsHelper);
\r
6356 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6358 if (*appData.icsHost == NULLCHAR) {
\r
6359 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6360 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6361 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6362 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6363 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6366 if (appData.icsActive) {
\r
6367 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6369 else if (appData.noChessProgram) {
\r
6370 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6373 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6376 SetStartupDialogEnables(hDlg);
\r
6380 switch (LOWORD(wParam)) {
\r
6382 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6383 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6384 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6386 comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox
\r
6387 ParseArgs(StringGet, &p);
\r
6388 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6389 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6391 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6392 ParseArgs(StringGet, &p);
\r
6393 SwapEngines(singleList); // ... and then make it 'second'
\r
6395 appData.noChessProgram = FALSE;
\r
6396 appData.icsActive = FALSE;
\r
6397 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6398 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6399 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6401 ParseArgs(StringGet, &p);
\r
6402 if (appData.zippyPlay) {
\r
6403 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6404 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6406 ParseArgs(StringGet, &p);
\r
6408 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6409 appData.noChessProgram = TRUE;
\r
6410 appData.icsActive = FALSE;
\r
6412 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6413 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6416 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6417 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6419 ParseArgs(StringGet, &p);
\r
6421 EndDialog(hDlg, TRUE);
\r
6428 case IDM_HELPCONTENTS:
\r
6429 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6430 MessageBox (GetFocus(),
\r
6431 _("Unable to activate help"),
\r
6432 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6437 SetStartupDialogEnables(hDlg);
\r
6445 /*---------------------------------------------------------------------------*\
\r
6447 * About box dialog functions
\r
6449 \*---------------------------------------------------------------------------*/
\r
6451 /* Process messages for "About" dialog box */
\r
6453 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6455 switch (message) {
\r
6456 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6457 /* Center the dialog over the application window */
\r
6458 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6459 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6460 Translate(hDlg, ABOUTBOX);
\r
6464 case WM_COMMAND: /* message: received a command */
\r
6465 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6466 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6467 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6475 /*---------------------------------------------------------------------------*\
\r
6477 * Comment Dialog functions
\r
6479 \*---------------------------------------------------------------------------*/
\r
6482 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6484 static HANDLE hwndText = NULL;
\r
6485 int len, newSizeX, newSizeY;
\r
6486 static int sizeX, sizeY;
\r
6491 switch (message) {
\r
6492 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6493 /* Initialize the dialog items */
\r
6494 Translate(hDlg, DLG_EditComment);
\r
6495 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6496 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6497 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6498 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6499 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6500 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6501 SetWindowText(hDlg, commentTitle);
\r
6502 if (editComment) {
\r
6503 SetFocus(hwndText);
\r
6505 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6507 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6508 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6509 MAKELPARAM(FALSE, 0));
\r
6510 /* Size and position the dialog */
\r
6511 if (!commentDialog) {
\r
6512 commentDialog = hDlg;
\r
6513 GetClientRect(hDlg, &rect);
\r
6514 sizeX = rect.right;
\r
6515 sizeY = rect.bottom;
\r
6516 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6517 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6518 WINDOWPLACEMENT wp;
\r
6519 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6520 wp.length = sizeof(WINDOWPLACEMENT);
\r
6522 wp.showCmd = SW_SHOW;
\r
6523 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6524 wp.rcNormalPosition.left = wpComment.x;
\r
6525 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6526 wp.rcNormalPosition.top = wpComment.y;
\r
6527 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6528 SetWindowPlacement(hDlg, &wp);
\r
6530 GetClientRect(hDlg, &rect);
\r
6531 newSizeX = rect.right;
\r
6532 newSizeY = rect.bottom;
\r
6533 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6534 newSizeX, newSizeY);
\r
6539 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6542 case WM_COMMAND: /* message: received a command */
\r
6543 switch (LOWORD(wParam)) {
\r
6545 if (editComment) {
\r
6547 /* Read changed options from the dialog box */
\r
6548 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6549 len = GetWindowTextLength(hwndText);
\r
6550 str = (char *) malloc(len + 1);
\r
6551 GetWindowText(hwndText, str, len + 1);
\r
6560 ReplaceComment(commentIndex, str);
\r
6567 case OPT_CancelComment:
\r
6571 case OPT_ClearComment:
\r
6572 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6575 case OPT_EditComment:
\r
6576 EditCommentEvent();
\r
6584 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6585 if( wParam == OPT_CommentText ) {
\r
6586 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6588 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6589 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6593 pt.x = LOWORD( lpMF->lParam );
\r
6594 pt.y = HIWORD( lpMF->lParam );
\r
6596 if(lpMF->msg == WM_CHAR) {
\r
6598 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6599 index = sel.cpMin;
\r
6601 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6603 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6604 len = GetWindowTextLength(hwndText);
\r
6605 str = (char *) malloc(len + 1);
\r
6606 GetWindowText(hwndText, str, len + 1);
\r
6607 ReplaceComment(commentIndex, str);
\r
6608 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6609 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6612 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6613 lpMF->msg = WM_USER;
\r
6621 newSizeX = LOWORD(lParam);
\r
6622 newSizeY = HIWORD(lParam);
\r
6623 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6628 case WM_GETMINMAXINFO:
\r
6629 /* Prevent resizing window too small */
\r
6630 mmi = (MINMAXINFO *) lParam;
\r
6631 mmi->ptMinTrackSize.x = 100;
\r
6632 mmi->ptMinTrackSize.y = 100;
\r
6639 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6644 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6646 if (str == NULL) str = "";
\r
6647 p = (char *) malloc(2 * strlen(str) + 2);
\r
6650 if (*str == '\n') *q++ = '\r';
\r
6654 if (commentText != NULL) free(commentText);
\r
6656 commentIndex = index;
\r
6657 commentTitle = title;
\r
6659 editComment = edit;
\r
6661 if (commentDialog) {
\r
6662 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6663 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6665 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6666 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6667 hwndMain, (DLGPROC)lpProc);
\r
6668 FreeProcInstance(lpProc);
\r
6674 /*---------------------------------------------------------------------------*\
\r
6676 * Type-in move dialog functions
\r
6678 \*---------------------------------------------------------------------------*/
\r
6681 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6683 char move[MSG_SIZ];
\r
6686 switch (message) {
\r
6687 case WM_INITDIALOG:
\r
6688 move[0] = (char) lParam;
\r
6689 move[1] = NULLCHAR;
\r
6690 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6691 Translate(hDlg, DLG_TypeInMove);
\r
6692 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6693 SetWindowText(hInput, move);
\r
6695 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6699 switch (LOWORD(wParam)) {
\r
6702 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6703 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6704 TypeInDoneEvent(move);
\r
6705 EndDialog(hDlg, TRUE);
\r
6708 EndDialog(hDlg, FALSE);
\r
6719 PopUpMoveDialog(char firstchar)
\r
6723 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6724 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6725 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6726 FreeProcInstance(lpProc);
\r
6729 /*---------------------------------------------------------------------------*\
\r
6731 * Type-in name dialog functions
\r
6733 \*---------------------------------------------------------------------------*/
\r
6736 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6738 char move[MSG_SIZ];
\r
6741 switch (message) {
\r
6742 case WM_INITDIALOG:
\r
6743 move[0] = (char) lParam;
\r
6744 move[1] = NULLCHAR;
\r
6745 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6746 Translate(hDlg, DLG_TypeInName);
\r
6747 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6748 SetWindowText(hInput, move);
\r
6750 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6754 switch (LOWORD(wParam)) {
\r
6756 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6757 appData.userName = strdup(move);
\r
6758 SetUserLogo(); DisplayLogos();
\r
6760 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6761 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6762 DisplayTitle(move);
\r
6766 EndDialog(hDlg, TRUE);
\r
6769 EndDialog(hDlg, FALSE);
\r
6780 PopUpNameDialog(char firstchar)
\r
6784 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6785 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6786 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6787 FreeProcInstance(lpProc);
\r
6790 /*---------------------------------------------------------------------------*\
\r
6794 \*---------------------------------------------------------------------------*/
\r
6796 /* Nonmodal error box */
\r
6797 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6798 WPARAM wParam, LPARAM lParam);
\r
6801 ErrorPopUp(char *title, char *content)
\r
6805 BOOLEAN modal = hwndMain == NULL;
\r
6823 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6824 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6827 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6829 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6830 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6831 hwndMain, (DLGPROC)lpProc);
\r
6832 FreeProcInstance(lpProc);
\r
6839 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6840 if (errorDialog == NULL) return;
\r
6841 DestroyWindow(errorDialog);
\r
6842 errorDialog = NULL;
\r
6843 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6847 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6851 switch (message) {
\r
6852 case WM_INITDIALOG:
\r
6853 GetWindowRect(hDlg, &rChild);
\r
6856 SetWindowPos(hDlg, NULL, rChild.left,
\r
6857 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6858 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6862 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6863 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6864 and it doesn't work when you resize the dialog.
\r
6865 For now, just give it a default position.
\r
6867 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6868 Translate(hDlg, DLG_Error);
\r
6870 errorDialog = hDlg;
\r
6871 SetWindowText(hDlg, errorTitle);
\r
6872 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6876 switch (LOWORD(wParam)) {
\r
6879 if (errorDialog == hDlg) errorDialog = NULL;
\r
6880 DestroyWindow(hDlg);
\r
6892 HWND gothicDialog = NULL;
\r
6895 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6898 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6900 switch (message) {
\r
6901 case WM_INITDIALOG:
\r
6902 GetWindowRect(hDlg, &rChild);
\r
6904 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6908 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6909 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6910 and it doesn't work when you resize the dialog.
\r
6911 For now, just give it a default position.
\r
6913 gothicDialog = hDlg;
\r
6914 SetWindowText(hDlg, errorTitle);
\r
6915 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6919 switch (LOWORD(wParam)) {
\r
6922 if (errorDialog == hDlg) errorDialog = NULL;
\r
6923 DestroyWindow(hDlg);
\r
6935 GothicPopUp(char *title, VariantClass variant)
\r
6938 static char *lastTitle;
\r
6940 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6941 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6943 if(lastTitle != title && gothicDialog != NULL) {
\r
6944 DestroyWindow(gothicDialog);
\r
6945 gothicDialog = NULL;
\r
6947 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6948 title = lastTitle;
\r
6949 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6950 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6951 hwndMain, (DLGPROC)lpProc);
\r
6952 FreeProcInstance(lpProc);
\r
6957 /*---------------------------------------------------------------------------*\
\r
6959 * Ics Interaction console functions
\r
6961 \*---------------------------------------------------------------------------*/
\r
6963 #define HISTORY_SIZE 64
\r
6964 static char *history[HISTORY_SIZE];
\r
6965 int histIn = 0, histP = 0;
\r
6969 SaveInHistory(char *cmd)
\r
6971 if (history[histIn] != NULL) {
\r
6972 free(history[histIn]);
\r
6973 history[histIn] = NULL;
\r
6975 if (*cmd == NULLCHAR) return;
\r
6976 history[histIn] = StrSave(cmd);
\r
6977 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6978 if (history[histIn] != NULL) {
\r
6979 free(history[histIn]);
\r
6981 history[histIn] = NULL;
\r
6987 PrevInHistory(char *cmd)
\r
6990 if (histP == histIn) {
\r
6991 if (history[histIn] != NULL) free(history[histIn]);
\r
6992 history[histIn] = StrSave(cmd);
\r
6994 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6995 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6997 return history[histP];
\r
7003 if (histP == histIn) return NULL;
\r
7004 histP = (histP + 1) % HISTORY_SIZE;
\r
7005 return history[histP];
\r
7009 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
7013 hmenu = LoadMenu(hInst, "TextMenu");
\r
7014 h = GetSubMenu(hmenu, 0);
\r
7016 if (strcmp(e->item, "-") == 0) {
\r
7017 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
7018 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
7019 int flags = MF_STRING, j = 0;
\r
7020 if (e->item[0] == '|') {
\r
7021 flags |= MF_MENUBARBREAK;
\r
7024 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
7025 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
7033 WNDPROC consoleTextWindowProc;
\r
7036 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
7038 char buf[MSG_SIZ], name[MSG_SIZ];
\r
7039 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7043 SetWindowText(hInput, command);
\r
7045 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
7047 sel.cpMin = 999999;
\r
7048 sel.cpMax = 999999;
\r
7049 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7054 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7055 if (sel.cpMin == sel.cpMax) {
\r
7056 /* Expand to surrounding word */
\r
7059 tr.chrg.cpMax = sel.cpMin;
\r
7060 tr.chrg.cpMin = --sel.cpMin;
\r
7061 if (sel.cpMin < 0) break;
\r
7062 tr.lpstrText = name;
\r
7063 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
7064 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
7068 tr.chrg.cpMin = sel.cpMax;
\r
7069 tr.chrg.cpMax = ++sel.cpMax;
\r
7070 tr.lpstrText = name;
\r
7071 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
7072 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
7075 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
7076 MessageBeep(MB_ICONEXCLAMATION);
\r
7080 tr.lpstrText = name;
\r
7081 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
7083 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
7084 MessageBeep(MB_ICONEXCLAMATION);
\r
7087 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
7090 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
7091 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
7092 SetWindowText(hInput, buf);
\r
7093 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
7095 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
7096 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
7097 SetWindowText(hInput, buf);
\r
7098 sel.cpMin = 999999;
\r
7099 sel.cpMax = 999999;
\r
7100 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7106 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7111 switch (message) {
\r
7113 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7114 if(wParam=='R') return 0;
\r
7117 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
7120 sel.cpMin = 999999;
\r
7121 sel.cpMax = 999999;
\r
7122 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7123 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
7128 if(wParam != '\022') {
\r
7129 if (wParam == '\t') {
\r
7130 if (GetKeyState(VK_SHIFT) < 0) {
\r
7132 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7133 if (buttonDesc[0].hwnd) {
\r
7134 SetFocus(buttonDesc[0].hwnd);
\r
7136 SetFocus(hwndMain);
\r
7140 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
7143 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7144 JAWS_DELETE( SetFocus(hInput); )
\r
7145 SendMessage(hInput, message, wParam, lParam);
\r
7148 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
7150 case WM_RBUTTONDOWN:
\r
7151 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
7152 /* Move selection here if it was empty */
\r
7154 pt.x = LOWORD(lParam);
\r
7155 pt.y = HIWORD(lParam);
\r
7156 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7157 if (sel.cpMin == sel.cpMax) {
\r
7158 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
7159 sel.cpMax = sel.cpMin;
\r
7160 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7162 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
7163 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
7165 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
7166 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7167 if (sel.cpMin == sel.cpMax) {
\r
7168 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7169 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
7171 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7172 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7174 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
7175 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
7176 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
7177 MenuPopup(hwnd, pt, hmenu, -1);
\r
7181 case WM_RBUTTONUP:
\r
7182 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7183 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7184 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7188 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7190 return SendMessage(hInput, message, wParam, lParam);
\r
7191 case WM_MBUTTONDOWN:
\r
7192 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7194 switch (LOWORD(wParam)) {
\r
7195 case IDM_QuickPaste:
\r
7197 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7198 if (sel.cpMin == sel.cpMax) {
\r
7199 MessageBeep(MB_ICONEXCLAMATION);
\r
7202 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7203 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7204 SendMessage(hInput, WM_PASTE, 0, 0);
\r
7209 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7212 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7215 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7219 int i = LOWORD(wParam) - IDM_CommandX;
\r
7220 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
7221 icsTextMenuEntry[i].command != NULL) {
\r
7222 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
7223 icsTextMenuEntry[i].getname,
\r
7224 icsTextMenuEntry[i].immediate);
\r
7232 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
7235 WNDPROC consoleInputWindowProc;
\r
7238 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7240 char buf[MSG_SIZ];
\r
7242 static BOOL sendNextChar = FALSE;
\r
7243 static BOOL quoteNextChar = FALSE;
\r
7244 InputSource *is = consoleInputSource;
\r
7248 switch (message) {
\r
7250 if (!appData.localLineEditing || sendNextChar) {
\r
7251 is->buf[0] = (CHAR) wParam;
\r
7253 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7254 sendNextChar = FALSE;
\r
7257 if (quoteNextChar) {
\r
7258 buf[0] = (char) wParam;
\r
7259 buf[1] = NULLCHAR;
\r
7260 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7261 quoteNextChar = FALSE;
\r
7265 case '\r': /* Enter key */
\r
7266 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7267 if (consoleEcho) SaveInHistory(is->buf);
\r
7268 is->buf[is->count++] = '\n';
\r
7269 is->buf[is->count] = NULLCHAR;
\r
7270 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7271 if (consoleEcho) {
\r
7272 ConsoleOutput(is->buf, is->count, TRUE);
\r
7273 } else if (appData.localLineEditing) {
\r
7274 ConsoleOutput("\n", 1, TRUE);
\r
7277 case '\033': /* Escape key */
\r
7278 SetWindowText(hwnd, "");
\r
7279 cf.cbSize = sizeof(CHARFORMAT);
\r
7280 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7281 if (consoleEcho) {
\r
7282 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7284 cf.crTextColor = COLOR_ECHOOFF;
\r
7286 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7287 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7289 case '\t': /* Tab key */
\r
7290 if (GetKeyState(VK_SHIFT) < 0) {
\r
7292 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7295 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7296 if (buttonDesc[0].hwnd) {
\r
7297 SetFocus(buttonDesc[0].hwnd);
\r
7299 SetFocus(hwndMain);
\r
7303 case '\023': /* Ctrl+S */
\r
7304 sendNextChar = TRUE;
\r
7306 case '\021': /* Ctrl+Q */
\r
7307 quoteNextChar = TRUE;
\r
7317 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7318 p = PrevInHistory(buf);
\r
7320 SetWindowText(hwnd, p);
\r
7321 sel.cpMin = 999999;
\r
7322 sel.cpMax = 999999;
\r
7323 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7328 p = NextInHistory();
\r
7330 SetWindowText(hwnd, p);
\r
7331 sel.cpMin = 999999;
\r
7332 sel.cpMax = 999999;
\r
7333 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7339 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7343 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7347 case WM_MBUTTONDOWN:
\r
7348 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7349 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7351 case WM_RBUTTONUP:
\r
7352 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7353 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7354 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7358 hmenu = LoadMenu(hInst, "InputMenu");
\r
7359 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7360 if (sel.cpMin == sel.cpMax) {
\r
7361 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7362 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7364 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7365 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7367 pt.x = LOWORD(lParam);
\r
7368 pt.y = HIWORD(lParam);
\r
7369 MenuPopup(hwnd, pt, hmenu, -1);
\r
7373 switch (LOWORD(wParam)) {
\r
7375 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7377 case IDM_SelectAll:
\r
7379 sel.cpMax = -1; /*999999?*/
\r
7380 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7383 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7386 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7389 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7394 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7397 #define CO_MAX 100000
\r
7398 #define CO_TRIM 1000
\r
7401 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7403 static SnapData sd;
\r
7404 HWND hText, hInput;
\r
7406 static int sizeX, sizeY;
\r
7407 int newSizeX, newSizeY;
\r
7411 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7412 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7414 switch (message) {
\r
7416 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7418 ENLINK *pLink = (ENLINK*)lParam;
\r
7419 if (pLink->msg == WM_LBUTTONUP)
\r
7423 tr.chrg = pLink->chrg;
\r
7424 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7425 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7426 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7427 free(tr.lpstrText);
\r
7431 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7432 hwndConsole = hDlg;
\r
7434 consoleTextWindowProc = (WNDPROC)
\r
7435 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7436 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7437 consoleInputWindowProc = (WNDPROC)
\r
7438 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7439 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7440 Colorize(ColorNormal, TRUE);
\r
7441 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7442 ChangedConsoleFont();
\r
7443 GetClientRect(hDlg, &rect);
\r
7444 sizeX = rect.right;
\r
7445 sizeY = rect.bottom;
\r
7446 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7447 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7448 WINDOWPLACEMENT wp;
\r
7449 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7450 wp.length = sizeof(WINDOWPLACEMENT);
\r
7452 wp.showCmd = SW_SHOW;
\r
7453 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7454 wp.rcNormalPosition.left = wpConsole.x;
\r
7455 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7456 wp.rcNormalPosition.top = wpConsole.y;
\r
7457 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7458 SetWindowPlacement(hDlg, &wp);
\r
7461 // [HGM] Chessknight's change 2004-07-13
\r
7462 else { /* Determine Defaults */
\r
7463 WINDOWPLACEMENT wp;
\r
7464 wpConsole.x = wpMain.width + 1;
\r
7465 wpConsole.y = wpMain.y;
\r
7466 wpConsole.width = screenWidth - wpMain.width;
\r
7467 wpConsole.height = wpMain.height;
\r
7468 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7469 wp.length = sizeof(WINDOWPLACEMENT);
\r
7471 wp.showCmd = SW_SHOW;
\r
7472 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7473 wp.rcNormalPosition.left = wpConsole.x;
\r
7474 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7475 wp.rcNormalPosition.top = wpConsole.y;
\r
7476 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7477 SetWindowPlacement(hDlg, &wp);
\r
7480 // Allow hText to highlight URLs and send notifications on them
\r
7481 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7482 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7483 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7484 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7498 if (IsIconic(hDlg)) break;
\r
7499 newSizeX = LOWORD(lParam);
\r
7500 newSizeY = HIWORD(lParam);
\r
7501 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7502 RECT rectText, rectInput;
\r
7504 int newTextHeight, newTextWidth;
\r
7505 GetWindowRect(hText, &rectText);
\r
7506 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7507 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7508 if (newTextHeight < 0) {
\r
7509 newSizeY += -newTextHeight;
\r
7510 newTextHeight = 0;
\r
7512 SetWindowPos(hText, NULL, 0, 0,
\r
7513 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7514 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7515 pt.x = rectInput.left;
\r
7516 pt.y = rectInput.top + newSizeY - sizeY;
\r
7517 ScreenToClient(hDlg, &pt);
\r
7518 SetWindowPos(hInput, NULL,
\r
7519 pt.x, pt.y, /* needs client coords */
\r
7520 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7521 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7527 case WM_GETMINMAXINFO:
\r
7528 /* Prevent resizing window too small */
\r
7529 mmi = (MINMAXINFO *) lParam;
\r
7530 mmi->ptMinTrackSize.x = 100;
\r
7531 mmi->ptMinTrackSize.y = 100;
\r
7534 /* [AS] Snapping */
\r
7535 case WM_ENTERSIZEMOVE:
\r
7536 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7539 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7542 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7544 case WM_EXITSIZEMOVE:
\r
7545 UpdateICSWidth(hText);
\r
7546 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7549 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7557 if (hwndConsole) return;
\r
7558 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7559 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7564 ConsoleOutput(char* data, int length, int forceVisible)
\r
7569 char buf[CO_MAX+1];
\r
7572 static int delayLF = 0;
\r
7573 CHARRANGE savesel, sel;
\r
7575 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7583 while (length--) {
\r
7591 } else if (*p == '\007') {
\r
7592 MyPlaySound(&sounds[(int)SoundBell]);
\r
7599 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7600 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7601 /* Save current selection */
\r
7602 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7603 exlen = GetWindowTextLength(hText);
\r
7604 /* Find out whether current end of text is visible */
\r
7605 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7606 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7607 /* Trim existing text if it's too long */
\r
7608 if (exlen + (q - buf) > CO_MAX) {
\r
7609 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7612 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7613 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7615 savesel.cpMin -= trim;
\r
7616 savesel.cpMax -= trim;
\r
7617 if (exlen < 0) exlen = 0;
\r
7618 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7619 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7621 /* Append the new text */
\r
7622 sel.cpMin = exlen;
\r
7623 sel.cpMax = exlen;
\r
7624 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7625 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7626 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7627 if (forceVisible || exlen == 0 ||
\r
7628 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7629 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7630 /* Scroll to make new end of text visible if old end of text
\r
7631 was visible or new text is an echo of user typein */
\r
7632 sel.cpMin = 9999999;
\r
7633 sel.cpMax = 9999999;
\r
7634 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7635 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7636 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7637 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7639 if (savesel.cpMax == exlen || forceVisible) {
\r
7640 /* Move insert point to new end of text if it was at the old
\r
7641 end of text or if the new text is an echo of user typein */
\r
7642 sel.cpMin = 9999999;
\r
7643 sel.cpMax = 9999999;
\r
7644 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7646 /* Restore previous selection */
\r
7647 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7649 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7656 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7660 COLORREF oldFg, oldBg;
\r
7664 if(copyNumber > 1)
\r
7665 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7667 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7668 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7669 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7672 rect.right = x + squareSize;
\r
7674 rect.bottom = y + squareSize;
\r
7677 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7678 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7679 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7680 &rect, str, strlen(str), NULL);
\r
7682 (void) SetTextColor(hdc, oldFg);
\r
7683 (void) SetBkColor(hdc, oldBg);
\r
7684 (void) SelectObject(hdc, oldFont);
\r
7688 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7689 RECT *rect, char *color, char *flagFell)
\r
7693 COLORREF oldFg, oldBg;
\r
7696 if (twoBoards && partnerUp) return;
\r
7697 if (appData.clockMode) {
\r
7698 if (tinyLayout == 2)
\r
7699 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7701 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7708 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7709 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7711 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7712 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7715 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7719 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7720 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7721 rect, str, strlen(str), NULL);
\r
7722 if(logoHeight > 0 && appData.clockMode) {
\r
7724 str += strlen(color)+2;
\r
7725 r.top = rect->top + logoHeight/2;
\r
7726 r.left = rect->left;
\r
7727 r.right = rect->right;
\r
7728 r.bottom = rect->bottom;
\r
7729 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7730 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7731 &r, str, strlen(str), NULL);
\r
7733 (void) SetTextColor(hdc, oldFg);
\r
7734 (void) SetBkColor(hdc, oldBg);
\r
7735 (void) SelectObject(hdc, oldFont);
\r
7740 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7746 if( count <= 0 ) {
\r
7747 if (appData.debugMode) {
\r
7748 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7751 return ERROR_INVALID_USER_BUFFER;
\r
7754 ResetEvent(ovl->hEvent);
\r
7755 ovl->Offset = ovl->OffsetHigh = 0;
\r
7756 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7760 err = GetLastError();
\r
7761 if (err == ERROR_IO_PENDING) {
\r
7762 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7766 err = GetLastError();
\r
7773 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7778 ResetEvent(ovl->hEvent);
\r
7779 ovl->Offset = ovl->OffsetHigh = 0;
\r
7780 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7784 err = GetLastError();
\r
7785 if (err == ERROR_IO_PENDING) {
\r
7786 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7790 err = GetLastError();
\r
7797 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7798 void CheckForInputBufferFull( InputSource * is )
\r
7800 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7801 /* Look for end of line */
\r
7802 char * p = is->buf;
\r
7804 while( p < is->next && *p != '\n' ) {
\r
7808 if( p >= is->next ) {
\r
7809 if (appData.debugMode) {
\r
7810 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7813 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7814 is->count = (DWORD) -1;
\r
7815 is->next = is->buf;
\r
7821 InputThread(LPVOID arg)
\r
7826 is = (InputSource *) arg;
\r
7827 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7828 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7829 while (is->hThread != NULL) {
\r
7830 is->error = DoReadFile(is->hFile, is->next,
\r
7831 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7832 &is->count, &ovl);
\r
7833 if (is->error == NO_ERROR) {
\r
7834 is->next += is->count;
\r
7836 if (is->error == ERROR_BROKEN_PIPE) {
\r
7837 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7840 is->count = (DWORD) -1;
\r
7841 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7846 CheckForInputBufferFull( is );
\r
7848 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7850 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7852 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7855 CloseHandle(ovl.hEvent);
\r
7856 CloseHandle(is->hFile);
\r
7858 if (appData.debugMode) {
\r
7859 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7866 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7868 NonOvlInputThread(LPVOID arg)
\r
7875 is = (InputSource *) arg;
\r
7876 while (is->hThread != NULL) {
\r
7877 is->error = ReadFile(is->hFile, is->next,
\r
7878 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7879 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7880 if (is->error == NO_ERROR) {
\r
7881 /* Change CRLF to LF */
\r
7882 if (is->next > is->buf) {
\r
7884 i = is->count + 1;
\r
7892 if (prev == '\r' && *p == '\n') {
\r
7904 if (is->error == ERROR_BROKEN_PIPE) {
\r
7905 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7908 is->count = (DWORD) -1;
\r
7912 CheckForInputBufferFull( is );
\r
7914 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7916 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7918 if (is->count < 0) break; /* Quit on error */
\r
7920 CloseHandle(is->hFile);
\r
7925 SocketInputThread(LPVOID arg)
\r
7929 is = (InputSource *) arg;
\r
7930 while (is->hThread != NULL) {
\r
7931 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7932 if ((int)is->count == SOCKET_ERROR) {
\r
7933 is->count = (DWORD) -1;
\r
7934 is->error = WSAGetLastError();
\r
7936 is->error = NO_ERROR;
\r
7937 is->next += is->count;
\r
7938 if (is->count == 0 && is->second == is) {
\r
7939 /* End of file on stderr; quit with no message */
\r
7943 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7945 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7947 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7953 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7957 is = (InputSource *) lParam;
\r
7958 if (is->lineByLine) {
\r
7959 /* Feed in lines one by one */
\r
7960 char *p = is->buf;
\r
7962 while (q < is->next) {
\r
7963 if (*q++ == '\n') {
\r
7964 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7969 /* Move any partial line to the start of the buffer */
\r
7971 while (p < is->next) {
\r
7976 if (is->error != NO_ERROR || is->count == 0) {
\r
7977 /* Notify backend of the error. Note: If there was a partial
\r
7978 line at the end, it is not flushed through. */
\r
7979 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7982 /* Feed in the whole chunk of input at once */
\r
7983 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7984 is->next = is->buf;
\r
7988 /*---------------------------------------------------------------------------*\
\r
7990 * Menu enables. Used when setting various modes.
\r
7992 \*---------------------------------------------------------------------------*/
\r
8000 GreyRevert(Boolean grey)
\r
8001 { // [HGM] vari: for retracting variations in local mode
\r
8002 HMENU hmenu = GetMenu(hwndMain);
\r
8003 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
8004 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
8008 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
8010 while (enab->item > 0) {
\r
8011 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
8016 Enables gnuEnables[] = {
\r
8017 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
8018 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
8019 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
8020 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
8021 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
8022 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
8023 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
8024 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
8025 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
8026 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
8027 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
8028 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
8029 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
8032 // Needed to switch from ncp to GNU mode on Engine Load
\r
8033 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
8034 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
8035 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
8036 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
8037 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
8038 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
8039 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
8040 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
8041 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
8042 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
8043 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
8044 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8045 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
8046 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
8050 Enables icsEnables[] = {
\r
8051 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
8052 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
8053 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8054 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8055 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8056 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8057 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
8058 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
8059 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
8060 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
8061 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8062 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
8063 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
8064 { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },
\r
8065 { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },
\r
8066 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
8067 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
8068 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
8069 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
8070 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
8075 Enables zippyEnables[] = {
\r
8076 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8077 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
8078 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
8079 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
8084 Enables ncpEnables[] = {
\r
8085 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
8086 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
8087 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8088 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8089 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8090 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8091 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
8092 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
8093 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
8094 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
8095 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
8096 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
8097 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8098 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
8099 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
8100 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
8101 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
8102 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
8103 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
8104 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
8105 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
8106 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
8110 Enables trainingOnEnables[] = {
\r
8111 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
8112 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
8113 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
8114 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
8115 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
8116 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
8117 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
8118 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8119 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
8123 Enables trainingOffEnables[] = {
\r
8124 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
8125 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
8126 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
8127 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
8128 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
8129 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
8130 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
8131 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8132 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
8136 /* These modify either ncpEnables or gnuEnables */
\r
8137 Enables cmailEnables[] = {
\r
8138 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
8139 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
8140 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
8141 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
8142 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
8143 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
8144 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
8148 Enables machineThinkingEnables[] = {
\r
8149 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8150 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
8151 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
8152 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8153 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
8154 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8155 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8156 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8157 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8158 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
8159 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8160 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8161 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8162 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8163 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
8164 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
8168 Enables userThinkingEnables[] = {
\r
8169 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8170 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
8171 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
8172 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8173 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
8174 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8175 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8176 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8177 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8178 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
8179 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
8180 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
8181 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
8182 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
8183 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
8184 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
8188 /*---------------------------------------------------------------------------*\
\r
8190 * Front-end interface functions exported by XBoard.
\r
8191 * Functions appear in same order as prototypes in frontend.h.
\r
8193 \*---------------------------------------------------------------------------*/
\r
8195 CheckMark(UINT item, int state)
\r
8197 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
8203 static UINT prevChecked = 0;
\r
8204 static int prevPausing = 0;
\r
8207 if (pausing != prevPausing) {
\r
8208 prevPausing = pausing;
\r
8209 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
8210 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
8211 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
8214 switch (gameMode) {
\r
8215 case BeginningOfGame:
\r
8216 if (appData.icsActive)
\r
8217 nowChecked = IDM_IcsClient;
\r
8218 else if (appData.noChessProgram)
\r
8219 nowChecked = IDM_EditGame;
\r
8221 nowChecked = IDM_MachineBlack;
\r
8223 case MachinePlaysBlack:
\r
8224 nowChecked = IDM_MachineBlack;
\r
8226 case MachinePlaysWhite:
\r
8227 nowChecked = IDM_MachineWhite;
\r
8229 case TwoMachinesPlay:
\r
8230 nowChecked = IDM_TwoMachines;
\r
8233 nowChecked = IDM_AnalysisMode;
\r
8236 nowChecked = IDM_AnalyzeFile;
\r
8239 nowChecked = IDM_EditGame;
\r
8241 case PlayFromGameFile:
\r
8242 nowChecked = IDM_LoadGame;
\r
8244 case EditPosition:
\r
8245 nowChecked = IDM_EditPosition;
\r
8248 nowChecked = IDM_Training;
\r
8250 case IcsPlayingWhite:
\r
8251 case IcsPlayingBlack:
\r
8252 case IcsObserving:
\r
8254 nowChecked = IDM_IcsClient;
\r
8261 if(prevChecked == IDM_TwoMachines) // [HGM] 'Machine Match' might have gotten disabled when stopping match
\r
8262 EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_ENABLED);
\r
8263 CheckMark(prevChecked, MF_UNCHECKED);
\r
8264 CheckMark(nowChecked, MF_CHECKED);
\r
8265 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8267 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8268 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8269 MF_BYCOMMAND|MF_ENABLED);
\r
8271 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8272 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8275 prevChecked = nowChecked;
\r
8277 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8278 if (appData.icsActive) {
\r
8279 if (appData.icsEngineAnalyze) {
\r
8280 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8282 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8285 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8291 HMENU hmenu = GetMenu(hwndMain);
\r
8292 SetMenuEnables(hmenu, icsEnables);
\r
8293 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8294 MF_BYCOMMAND|MF_ENABLED);
\r
8296 if (appData.zippyPlay) {
\r
8297 SetMenuEnables(hmenu, zippyEnables);
\r
8298 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8299 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8300 MF_BYCOMMAND|MF_ENABLED);
\r
8308 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8314 HMENU hmenu = GetMenu(hwndMain);
\r
8315 SetMenuEnables(hmenu, ncpEnables);
\r
8316 DrawMenuBar(hwndMain);
\r
8322 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8326 SetTrainingModeOn()
\r
8329 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8330 for (i = 0; i < N_BUTTONS; i++) {
\r
8331 if (buttonDesc[i].hwnd != NULL)
\r
8332 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8337 VOID SetTrainingModeOff()
\r
8340 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8341 for (i = 0; i < N_BUTTONS; i++) {
\r
8342 if (buttonDesc[i].hwnd != NULL)
\r
8343 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8349 SetUserThinkingEnables()
\r
8351 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8355 SetMachineThinkingEnables()
\r
8357 HMENU hMenu = GetMenu(hwndMain);
\r
8358 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8360 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8362 if (gameMode == MachinePlaysBlack) {
\r
8363 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8364 } else if (gameMode == MachinePlaysWhite) {
\r
8365 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8366 } else if (gameMode == TwoMachinesPlay) {
\r
8367 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8373 DisplayTitle(char *str)
\r
8375 char title[MSG_SIZ], *host;
\r
8376 if (str[0] != NULLCHAR) {
\r
8377 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8378 } else if (appData.icsActive) {
\r
8379 if (appData.icsCommPort[0] != NULLCHAR)
\r
8382 host = appData.icsHost;
\r
8383 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8384 } else if (appData.noChessProgram) {
\r
8385 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8387 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8388 strcat(title, ": ");
\r
8389 strcat(title, first.tidy);
\r
8391 SetWindowText(hwndMain, title);
\r
8396 DisplayMessage(char *str1, char *str2)
\r
8400 int remain = MESSAGE_TEXT_MAX - 1;
\r
8403 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8404 messageText[0] = NULLCHAR;
\r
8406 len = strlen(str1);
\r
8407 if (len > remain) len = remain;
\r
8408 strncpy(messageText, str1, len);
\r
8409 messageText[len] = NULLCHAR;
\r
8412 if (*str2 && remain >= 2) {
\r
8414 strcat(messageText, " ");
\r
8417 len = strlen(str2);
\r
8418 if (len > remain) len = remain;
\r
8419 strncat(messageText, str2, len);
\r
8421 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8422 safeStrCpy(lastMsg, messageText, MSG_SIZ);
\r
8424 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8428 hdc = GetDC(hwndMain);
\r
8429 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8430 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8431 &messageRect, messageText, strlen(messageText), NULL);
\r
8432 (void) SelectObject(hdc, oldFont);
\r
8433 (void) ReleaseDC(hwndMain, hdc);
\r
8437 DisplayError(char *str, int error)
\r
8439 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8443 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8445 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8446 NULL, error, LANG_NEUTRAL,
\r
8447 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8449 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8451 ErrorMap *em = errmap;
\r
8452 while (em->err != 0 && em->err != error) em++;
\r
8453 if (em->err != 0) {
\r
8454 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8456 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8461 ErrorPopUp(_("Error"), buf);
\r
8466 DisplayMoveError(char *str)
\r
8468 fromX = fromY = -1;
\r
8469 ClearHighlights();
\r
8470 DrawPosition(FALSE, NULL);
\r
8471 if (appData.popupMoveErrors) {
\r
8472 ErrorPopUp(_("Error"), str);
\r
8474 DisplayMessage(str, "");
\r
8475 moveErrorMessageUp = TRUE;
\r
8480 DisplayFatalError(char *str, int error, int exitStatus)
\r
8482 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8484 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8487 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8488 NULL, error, LANG_NEUTRAL,
\r
8489 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8491 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8493 ErrorMap *em = errmap;
\r
8494 while (em->err != 0 && em->err != error) em++;
\r
8495 if (em->err != 0) {
\r
8496 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8498 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8503 if (appData.debugMode) {
\r
8504 fprintf(debugFP, "%s: %s\n", label, str);
\r
8506 if (appData.popupExitMessage) {
\r
8507 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8508 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8510 ExitEvent(exitStatus);
\r
8515 DisplayInformation(char *str)
\r
8517 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8522 DisplayNote(char *str)
\r
8524 ErrorPopUp(_("Note"), str);
\r
8529 char *title, *question, *replyPrefix;
\r
8534 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8536 static QuestionParams *qp;
\r
8537 char reply[MSG_SIZ];
\r
8540 switch (message) {
\r
8541 case WM_INITDIALOG:
\r
8542 qp = (QuestionParams *) lParam;
\r
8543 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8544 Translate(hDlg, DLG_Question);
\r
8545 SetWindowText(hDlg, qp->title);
\r
8546 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8547 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8551 switch (LOWORD(wParam)) {
\r
8553 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8554 if (*reply) strcat(reply, " ");
\r
8555 len = strlen(reply);
\r
8556 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8557 strcat(reply, "\n");
\r
8558 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8559 EndDialog(hDlg, TRUE);
\r
8560 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8563 EndDialog(hDlg, FALSE);
\r
8574 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8576 QuestionParams qp;
\r
8580 qp.question = question;
\r
8581 qp.replyPrefix = replyPrefix;
\r
8583 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8584 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8585 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8586 FreeProcInstance(lpProc);
\r
8589 /* [AS] Pick FRC position */
\r
8590 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8592 static int * lpIndexFRC;
\r
8598 case WM_INITDIALOG:
\r
8599 lpIndexFRC = (int *) lParam;
\r
8601 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8602 Translate(hDlg, DLG_NewGameFRC);
\r
8604 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8605 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8606 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8607 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8612 switch( LOWORD(wParam) ) {
\r
8614 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8615 EndDialog( hDlg, 0 );
\r
8616 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8619 EndDialog( hDlg, 1 );
\r
8621 case IDC_NFG_Edit:
\r
8622 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8623 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8625 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8628 case IDC_NFG_Random:
\r
8629 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8630 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8643 int index = appData.defaultFrcPosition;
\r
8644 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8646 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8648 if( result == 0 ) {
\r
8649 appData.defaultFrcPosition = index;
\r
8655 /* [AS] Game list options. Refactored by HGM */
\r
8657 HWND gameListOptionsDialog;
\r
8659 // low-level front-end: clear text edit / list widget
\r
8664 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8667 // low-level front-end: clear text edit / list widget
\r
8669 GLT_DeSelectList()
\r
8671 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8674 // low-level front-end: append line to text edit / list widget
\r
8676 GLT_AddToList( char *name )
\r
8679 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8683 // low-level front-end: get line from text edit / list widget
\r
8685 GLT_GetFromList( int index, char *name )
\r
8688 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8694 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8696 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8697 int idx2 = idx1 + delta;
\r
8698 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8700 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8703 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8704 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8705 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8706 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8710 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8714 case WM_INITDIALOG:
\r
8715 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8717 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8718 Translate(hDlg, DLG_GameListOptions);
\r
8720 /* Initialize list */
\r
8721 GLT_TagsToList( lpUserGLT );
\r
8723 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8728 switch( LOWORD(wParam) ) {
\r
8731 EndDialog( hDlg, 0 );
\r
8734 EndDialog( hDlg, 1 );
\r
8737 case IDC_GLT_Default:
\r
8738 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8741 case IDC_GLT_Restore:
\r
8742 GLT_TagsToList( appData.gameListTags );
\r
8746 GLT_MoveSelection( hDlg, -1 );
\r
8749 case IDC_GLT_Down:
\r
8750 GLT_MoveSelection( hDlg, +1 );
\r
8760 int GameListOptions()
\r
8763 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8765 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8767 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8769 if( result == 0 ) {
\r
8770 char *oldTags = appData.gameListTags;
\r
8771 /* [AS] Memory leak here! */
\r
8772 appData.gameListTags = strdup( lpUserGLT );
\r
8773 if(strcmp(oldTags, appData.gameListTags)) // [HGM] redo Game List when we changed something
\r
8774 GameListToListBox(NULL, TRUE, ".", NULL, FALSE, FALSE); // "." as filter is kludge to select all
\r
8781 DisplayIcsInteractionTitle(char *str)
\r
8783 char consoleTitle[MSG_SIZ];
\r
8785 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8786 SetWindowText(hwndConsole, consoleTitle);
\r
8788 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
8789 char buf[MSG_SIZ], *p = buf, *q;
\r
8790 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
8792 q = strchr(p, ';');
\r
8794 if(*p) ChatPopUp(p);
\r
8798 SetActiveWindow(hwndMain);
\r
8802 DrawPosition(int fullRedraw, Board board)
\r
8804 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8807 void NotifyFrontendLogin()
\r
8810 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8816 fromX = fromY = -1;
\r
8817 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8818 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8819 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8820 dragInfo.lastpos = dragInfo.pos;
\r
8821 dragInfo.start.x = dragInfo.start.y = -1;
\r
8822 dragInfo.from = dragInfo.start;
\r
8824 DrawPosition(TRUE, NULL);
\r
8831 CommentPopUp(char *title, char *str)
\r
8833 HWND hwnd = GetActiveWindow();
\r
8834 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8836 SetActiveWindow(hwnd);
\r
8840 CommentPopDown(void)
\r
8842 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8843 if (commentDialog) {
\r
8844 ShowWindow(commentDialog, SW_HIDE);
\r
8846 commentUp = FALSE;
\r
8850 EditCommentPopUp(int index, char *title, char *str)
\r
8852 EitherCommentPopUp(index, title, str, TRUE);
\r
8859 MyPlaySound(&sounds[(int)SoundRoar]);
\r
8866 MyPlaySound(&sounds[(int)SoundMove]);
\r
8869 VOID PlayIcsWinSound()
\r
8871 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8874 VOID PlayIcsLossSound()
\r
8876 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8879 VOID PlayIcsDrawSound()
\r
8881 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8884 VOID PlayIcsUnfinishedSound()
\r
8886 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8892 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8898 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8906 consoleEcho = TRUE;
\r
8907 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8908 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8909 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8918 consoleEcho = FALSE;
\r
8919 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8920 /* This works OK: set text and background both to the same color */
\r
8922 cf.crTextColor = COLOR_ECHOOFF;
\r
8923 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8924 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8927 /* No Raw()...? */
\r
8929 void Colorize(ColorClass cc, int continuation)
\r
8931 currentColorClass = cc;
\r
8932 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8933 consoleCF.crTextColor = textAttribs[cc].color;
\r
8934 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8935 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8941 static char buf[MSG_SIZ];
\r
8942 DWORD bufsiz = MSG_SIZ;
\r
8944 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8945 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8947 if (!GetUserName(buf, &bufsiz)) {
\r
8948 /*DisplayError("Error getting user name", GetLastError());*/
\r
8949 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8957 static char buf[MSG_SIZ];
\r
8958 DWORD bufsiz = MSG_SIZ;
\r
8960 if (!GetComputerName(buf, &bufsiz)) {
\r
8961 /*DisplayError("Error getting host name", GetLastError());*/
\r
8962 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8969 ClockTimerRunning()
\r
8971 return clockTimerEvent != 0;
\r
8977 if (clockTimerEvent == 0) return FALSE;
\r
8978 KillTimer(hwndMain, clockTimerEvent);
\r
8979 clockTimerEvent = 0;
\r
8984 StartClockTimer(long millisec)
\r
8986 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8987 (UINT) millisec, NULL);
\r
8991 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8994 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8996 if(appData.noGUI) return;
\r
8997 hdc = GetDC(hwndMain);
\r
8998 if (!IsIconic(hwndMain)) {
\r
8999 DisplayAClock(hdc, timeRemaining, highlight,
\r
9000 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
9002 if (highlight && iconCurrent == iconBlack) {
\r
9003 iconCurrent = iconWhite;
\r
9004 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
9005 if (IsIconic(hwndMain)) {
\r
9006 DrawIcon(hdc, 2, 2, iconCurrent);
\r
9009 (void) ReleaseDC(hwndMain, hdc);
\r
9011 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
9015 DisplayBlackClock(long timeRemaining, int highlight)
\r
9018 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
9021 if(appData.noGUI) return;
\r
9022 hdc = GetDC(hwndMain);
\r
9023 if (!IsIconic(hwndMain)) {
\r
9024 DisplayAClock(hdc, timeRemaining, highlight,
\r
9025 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
9027 if (highlight && iconCurrent == iconWhite) {
\r
9028 iconCurrent = iconBlack;
\r
9029 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
9030 if (IsIconic(hwndMain)) {
\r
9031 DrawIcon(hdc, 2, 2, iconCurrent);
\r
9034 (void) ReleaseDC(hwndMain, hdc);
\r
9036 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
9041 LoadGameTimerRunning()
\r
9043 return loadGameTimerEvent != 0;
\r
9047 StopLoadGameTimer()
\r
9049 if (loadGameTimerEvent == 0) return FALSE;
\r
9050 KillTimer(hwndMain, loadGameTimerEvent);
\r
9051 loadGameTimerEvent = 0;
\r
9056 StartLoadGameTimer(long millisec)
\r
9058 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
9059 (UINT) millisec, NULL);
\r
9067 char fileTitle[MSG_SIZ];
\r
9069 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
9070 f = OpenFileDialog(hwndMain, "a", defName,
\r
9071 appData.oldSaveStyle ? "gam" : "pgn",
\r
9073 _("Save Game to File"), NULL, fileTitle, NULL);
\r
9075 SaveGame(f, 0, "");
\r
9082 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
9084 if (delayedTimerEvent != 0) {
\r
9085 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
9086 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
9088 KillTimer(hwndMain, delayedTimerEvent);
\r
9089 delayedTimerEvent = 0;
\r
9090 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
9091 delayedTimerCallback();
\r
9093 delayedTimerCallback = cb;
\r
9094 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
9095 (UINT) millisec, NULL);
\r
9098 DelayedEventCallback
\r
9101 if (delayedTimerEvent) {
\r
9102 return delayedTimerCallback;
\r
9109 CancelDelayedEvent()
\r
9111 if (delayedTimerEvent) {
\r
9112 KillTimer(hwndMain, delayedTimerEvent);
\r
9113 delayedTimerEvent = 0;
\r
9117 DWORD GetWin32Priority(int nice)
\r
9118 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
9120 REALTIME_PRIORITY_CLASS 0x00000100
\r
9121 HIGH_PRIORITY_CLASS 0x00000080
\r
9122 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
9123 NORMAL_PRIORITY_CLASS 0x00000020
\r
9124 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
9125 IDLE_PRIORITY_CLASS 0x00000040
\r
9127 if (nice < -15) return 0x00000080;
\r
9128 if (nice < 0) return 0x00008000;
\r
9129 if (nice == 0) return 0x00000020;
\r
9130 if (nice < 15) return 0x00004000;
\r
9131 return 0x00000040;
\r
9134 void RunCommand(char *cmdLine)
\r
9136 /* Now create the child process. */
\r
9137 STARTUPINFO siStartInfo;
\r
9138 PROCESS_INFORMATION piProcInfo;
\r
9140 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9141 siStartInfo.lpReserved = NULL;
\r
9142 siStartInfo.lpDesktop = NULL;
\r
9143 siStartInfo.lpTitle = NULL;
\r
9144 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9145 siStartInfo.cbReserved2 = 0;
\r
9146 siStartInfo.lpReserved2 = NULL;
\r
9147 siStartInfo.hStdInput = NULL;
\r
9148 siStartInfo.hStdOutput = NULL;
\r
9149 siStartInfo.hStdError = NULL;
\r
9151 CreateProcess(NULL,
\r
9152 cmdLine, /* command line */
\r
9153 NULL, /* process security attributes */
\r
9154 NULL, /* primary thread security attrs */
\r
9155 TRUE, /* handles are inherited */
\r
9156 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9157 NULL, /* use parent's environment */
\r
9159 &siStartInfo, /* STARTUPINFO pointer */
\r
9160 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9162 CloseHandle(piProcInfo.hThread);
\r
9165 /* Start a child process running the given program.
\r
9166 The process's standard output can be read from "from", and its
\r
9167 standard input can be written to "to".
\r
9168 Exit with fatal error if anything goes wrong.
\r
9169 Returns an opaque pointer that can be used to destroy the process
\r
9173 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
9175 #define BUFSIZE 4096
\r
9177 HANDLE hChildStdinRd, hChildStdinWr,
\r
9178 hChildStdoutRd, hChildStdoutWr;
\r
9179 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
9180 SECURITY_ATTRIBUTES saAttr;
\r
9182 PROCESS_INFORMATION piProcInfo;
\r
9183 STARTUPINFO siStartInfo;
\r
9185 char buf[MSG_SIZ];
\r
9188 if (appData.debugMode) {
\r
9189 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
9194 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
9195 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
9196 saAttr.bInheritHandle = TRUE;
\r
9197 saAttr.lpSecurityDescriptor = NULL;
\r
9200 * The steps for redirecting child's STDOUT:
\r
9201 * 1. Create anonymous pipe to be STDOUT for child.
\r
9202 * 2. Create a noninheritable duplicate of read handle,
\r
9203 * and close the inheritable read handle.
\r
9206 /* Create a pipe for the child's STDOUT. */
\r
9207 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
9208 return GetLastError();
\r
9211 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
9212 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
9213 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
9214 FALSE, /* not inherited */
\r
9215 DUPLICATE_SAME_ACCESS);
\r
9217 return GetLastError();
\r
9219 CloseHandle(hChildStdoutRd);
\r
9222 * The steps for redirecting child's STDIN:
\r
9223 * 1. Create anonymous pipe to be STDIN for child.
\r
9224 * 2. Create a noninheritable duplicate of write handle,
\r
9225 * and close the inheritable write handle.
\r
9228 /* Create a pipe for the child's STDIN. */
\r
9229 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
9230 return GetLastError();
\r
9233 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
9234 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
9235 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
9236 FALSE, /* not inherited */
\r
9237 DUPLICATE_SAME_ACCESS);
\r
9239 return GetLastError();
\r
9241 CloseHandle(hChildStdinWr);
\r
9243 /* Arrange to (1) look in dir for the child .exe file, and
\r
9244 * (2) have dir be the child's working directory. Interpret
\r
9245 * dir relative to the directory WinBoard loaded from. */
\r
9246 GetCurrentDirectory(MSG_SIZ, buf);
\r
9247 SetCurrentDirectory(installDir);
\r
9248 SetCurrentDirectory(dir);
\r
9250 /* Now create the child process. */
\r
9252 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9253 siStartInfo.lpReserved = NULL;
\r
9254 siStartInfo.lpDesktop = NULL;
\r
9255 siStartInfo.lpTitle = NULL;
\r
9256 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9257 siStartInfo.cbReserved2 = 0;
\r
9258 siStartInfo.lpReserved2 = NULL;
\r
9259 siStartInfo.hStdInput = hChildStdinRd;
\r
9260 siStartInfo.hStdOutput = hChildStdoutWr;
\r
9261 siStartInfo.hStdError = hChildStdoutWr;
\r
9263 fSuccess = CreateProcess(NULL,
\r
9264 cmdLine, /* command line */
\r
9265 NULL, /* process security attributes */
\r
9266 NULL, /* primary thread security attrs */
\r
9267 TRUE, /* handles are inherited */
\r
9268 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9269 NULL, /* use parent's environment */
\r
9271 &siStartInfo, /* STARTUPINFO pointer */
\r
9272 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9274 err = GetLastError();
\r
9275 SetCurrentDirectory(buf); /* return to prev directory */
\r
9280 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
9281 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
9282 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
9285 /* Close the handles we don't need in the parent */
\r
9286 CloseHandle(piProcInfo.hThread);
\r
9287 CloseHandle(hChildStdinRd);
\r
9288 CloseHandle(hChildStdoutWr);
\r
9290 /* Prepare return value */
\r
9291 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9292 cp->kind = CPReal;
\r
9293 cp->hProcess = piProcInfo.hProcess;
\r
9294 cp->pid = piProcInfo.dwProcessId;
\r
9295 cp->hFrom = hChildStdoutRdDup;
\r
9296 cp->hTo = hChildStdinWrDup;
\r
9298 *pr = (void *) cp;
\r
9300 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
9301 2000 where engines sometimes don't see the initial command(s)
\r
9302 from WinBoard and hang. I don't understand how that can happen,
\r
9303 but the Sleep is harmless, so I've put it in. Others have also
\r
9304 reported what may be the same problem, so hopefully this will fix
\r
9305 it for them too. */
\r
9313 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
9315 ChildProc *cp; int result;
\r
9317 cp = (ChildProc *) pr;
\r
9318 if (cp == NULL) return;
\r
9320 switch (cp->kind) {
\r
9322 /* TerminateProcess is considered harmful, so... */
\r
9323 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9324 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9325 /* The following doesn't work because the chess program
\r
9326 doesn't "have the same console" as WinBoard. Maybe
\r
9327 we could arrange for this even though neither WinBoard
\r
9328 nor the chess program uses a console for stdio? */
\r
9329 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9331 /* [AS] Special termination modes for misbehaving programs... */
\r
9332 if( signal & 8 ) {
\r
9333 result = TerminateProcess( cp->hProcess, 0 );
\r
9335 if ( appData.debugMode) {
\r
9336 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9339 else if( signal & 4 ) {
\r
9340 DWORD dw = WaitForSingleObject( cp->hProcess, appData.delayAfterQuit*1000 + 50 ); // Wait 3 seconds at most
\r
9342 if( dw != WAIT_OBJECT_0 ) {
\r
9343 result = TerminateProcess( cp->hProcess, 0 );
\r
9345 if ( appData.debugMode) {
\r
9346 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9352 CloseHandle(cp->hProcess);
\r
9356 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9360 closesocket(cp->sock);
\r
9365 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9366 closesocket(cp->sock);
\r
9367 closesocket(cp->sock2);
\r
9375 InterruptChildProcess(ProcRef pr)
\r
9379 cp = (ChildProc *) pr;
\r
9380 if (cp == NULL) return;
\r
9381 switch (cp->kind) {
\r
9383 /* The following doesn't work because the chess program
\r
9384 doesn't "have the same console" as WinBoard. Maybe
\r
9385 we could arrange for this even though neither WinBoard
\r
9386 nor the chess program uses a console for stdio */
\r
9387 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9392 /* Can't interrupt */
\r
9396 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9403 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9405 char cmdLine[MSG_SIZ];
\r
9407 if (port[0] == NULLCHAR) {
\r
9408 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9410 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9412 return StartChildProcess(cmdLine, "", pr);
\r
9416 /* Code to open TCP sockets */
\r
9419 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9425 struct sockaddr_in sa, mysa;
\r
9426 struct hostent FAR *hp;
\r
9427 unsigned short uport;
\r
9428 WORD wVersionRequested;
\r
9431 /* Initialize socket DLL */
\r
9432 wVersionRequested = MAKEWORD(1, 1);
\r
9433 err = WSAStartup(wVersionRequested, &wsaData);
\r
9434 if (err != 0) return err;
\r
9437 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9438 err = WSAGetLastError();
\r
9443 /* Bind local address using (mostly) don't-care values.
\r
9445 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9446 mysa.sin_family = AF_INET;
\r
9447 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9448 uport = (unsigned short) 0;
\r
9449 mysa.sin_port = htons(uport);
\r
9450 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9451 == SOCKET_ERROR) {
\r
9452 err = WSAGetLastError();
\r
9457 /* Resolve remote host name */
\r
9458 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9459 if (!(hp = gethostbyname(host))) {
\r
9460 unsigned int b0, b1, b2, b3;
\r
9462 err = WSAGetLastError();
\r
9464 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9465 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9466 hp->h_addrtype = AF_INET;
\r
9468 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9469 hp->h_addr_list[0] = (char *) malloc(4);
\r
9470 hp->h_addr_list[0][0] = (char) b0;
\r
9471 hp->h_addr_list[0][1] = (char) b1;
\r
9472 hp->h_addr_list[0][2] = (char) b2;
\r
9473 hp->h_addr_list[0][3] = (char) b3;
\r
9479 sa.sin_family = hp->h_addrtype;
\r
9480 uport = (unsigned short) atoi(port);
\r
9481 sa.sin_port = htons(uport);
\r
9482 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9484 /* Make connection */
\r
9485 if (connect(s, (struct sockaddr *) &sa,
\r
9486 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9487 err = WSAGetLastError();
\r
9492 /* Prepare return value */
\r
9493 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9494 cp->kind = CPSock;
\r
9496 *pr = (ProcRef *) cp;
\r
9502 OpenCommPort(char *name, ProcRef *pr)
\r
9507 char fullname[MSG_SIZ];
\r
9509 if (*name != '\\')
\r
9510 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9512 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9514 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9515 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9516 if (h == (HANDLE) -1) {
\r
9517 return GetLastError();
\r
9521 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9523 /* Accumulate characters until a 100ms pause, then parse */
\r
9524 ct.ReadIntervalTimeout = 100;
\r
9525 ct.ReadTotalTimeoutMultiplier = 0;
\r
9526 ct.ReadTotalTimeoutConstant = 0;
\r
9527 ct.WriteTotalTimeoutMultiplier = 0;
\r
9528 ct.WriteTotalTimeoutConstant = 0;
\r
9529 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9531 /* Prepare return value */
\r
9532 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9533 cp->kind = CPComm;
\r
9536 *pr = (ProcRef *) cp;
\r
9542 OpenLoopback(ProcRef *pr)
\r
9544 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9550 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9555 struct sockaddr_in sa, mysa;
\r
9556 struct hostent FAR *hp;
\r
9557 unsigned short uport;
\r
9558 WORD wVersionRequested;
\r
9561 char stderrPortStr[MSG_SIZ];
\r
9563 /* Initialize socket DLL */
\r
9564 wVersionRequested = MAKEWORD(1, 1);
\r
9565 err = WSAStartup(wVersionRequested, &wsaData);
\r
9566 if (err != 0) return err;
\r
9568 /* Resolve remote host name */
\r
9569 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9570 if (!(hp = gethostbyname(host))) {
\r
9571 unsigned int b0, b1, b2, b3;
\r
9573 err = WSAGetLastError();
\r
9575 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9576 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9577 hp->h_addrtype = AF_INET;
\r
9579 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9580 hp->h_addr_list[0] = (char *) malloc(4);
\r
9581 hp->h_addr_list[0][0] = (char) b0;
\r
9582 hp->h_addr_list[0][1] = (char) b1;
\r
9583 hp->h_addr_list[0][2] = (char) b2;
\r
9584 hp->h_addr_list[0][3] = (char) b3;
\r
9590 sa.sin_family = hp->h_addrtype;
\r
9591 uport = (unsigned short) 514;
\r
9592 sa.sin_port = htons(uport);
\r
9593 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9595 /* Bind local socket to unused "privileged" port address
\r
9597 s = INVALID_SOCKET;
\r
9598 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9599 mysa.sin_family = AF_INET;
\r
9600 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9601 for (fromPort = 1023;; fromPort--) {
\r
9602 if (fromPort < 0) {
\r
9604 return WSAEADDRINUSE;
\r
9606 if (s == INVALID_SOCKET) {
\r
9607 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9608 err = WSAGetLastError();
\r
9613 uport = (unsigned short) fromPort;
\r
9614 mysa.sin_port = htons(uport);
\r
9615 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9616 == SOCKET_ERROR) {
\r
9617 err = WSAGetLastError();
\r
9618 if (err == WSAEADDRINUSE) continue;
\r
9622 if (connect(s, (struct sockaddr *) &sa,
\r
9623 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9624 err = WSAGetLastError();
\r
9625 if (err == WSAEADDRINUSE) {
\r
9636 /* Bind stderr local socket to unused "privileged" port address
\r
9638 s2 = INVALID_SOCKET;
\r
9639 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9640 mysa.sin_family = AF_INET;
\r
9641 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9642 for (fromPort = 1023;; fromPort--) {
\r
9643 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9644 if (fromPort < 0) {
\r
9645 (void) closesocket(s);
\r
9647 return WSAEADDRINUSE;
\r
9649 if (s2 == INVALID_SOCKET) {
\r
9650 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9651 err = WSAGetLastError();
\r
9657 uport = (unsigned short) fromPort;
\r
9658 mysa.sin_port = htons(uport);
\r
9659 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9660 == SOCKET_ERROR) {
\r
9661 err = WSAGetLastError();
\r
9662 if (err == WSAEADDRINUSE) continue;
\r
9663 (void) closesocket(s);
\r
9667 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9668 err = WSAGetLastError();
\r
9669 if (err == WSAEADDRINUSE) {
\r
9671 s2 = INVALID_SOCKET;
\r
9674 (void) closesocket(s);
\r
9675 (void) closesocket(s2);
\r
9681 prevStderrPort = fromPort; // remember port used
\r
9682 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9684 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9685 err = WSAGetLastError();
\r
9686 (void) closesocket(s);
\r
9687 (void) closesocket(s2);
\r
9692 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9693 err = WSAGetLastError();
\r
9694 (void) closesocket(s);
\r
9695 (void) closesocket(s2);
\r
9699 if (*user == NULLCHAR) user = UserName();
\r
9700 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9701 err = WSAGetLastError();
\r
9702 (void) closesocket(s);
\r
9703 (void) closesocket(s2);
\r
9707 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9708 err = WSAGetLastError();
\r
9709 (void) closesocket(s);
\r
9710 (void) closesocket(s2);
\r
9715 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9716 err = WSAGetLastError();
\r
9717 (void) closesocket(s);
\r
9718 (void) closesocket(s2);
\r
9722 (void) closesocket(s2); /* Stop listening */
\r
9724 /* Prepare return value */
\r
9725 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9726 cp->kind = CPRcmd;
\r
9729 *pr = (ProcRef *) cp;
\r
9736 AddInputSource(ProcRef pr, int lineByLine,
\r
9737 InputCallback func, VOIDSTAR closure)
\r
9739 InputSource *is, *is2 = NULL;
\r
9740 ChildProc *cp = (ChildProc *) pr;
\r
9742 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9743 is->lineByLine = lineByLine;
\r
9745 is->closure = closure;
\r
9746 is->second = NULL;
\r
9747 is->next = is->buf;
\r
9748 if (pr == NoProc) {
\r
9749 is->kind = CPReal;
\r
9750 consoleInputSource = is;
\r
9752 is->kind = cp->kind;
\r
9754 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9755 we create all threads suspended so that the is->hThread variable can be
\r
9756 safely assigned, then let the threads start with ResumeThread.
\r
9758 switch (cp->kind) {
\r
9760 is->hFile = cp->hFrom;
\r
9761 cp->hFrom = NULL; /* now owned by InputThread */
\r
9763 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9764 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9768 is->hFile = cp->hFrom;
\r
9769 cp->hFrom = NULL; /* now owned by InputThread */
\r
9771 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9772 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9776 is->sock = cp->sock;
\r
9778 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9779 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9783 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9785 is->sock = cp->sock;
\r
9787 is2->sock = cp->sock2;
\r
9788 is2->second = is2;
\r
9790 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9791 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9793 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9794 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9798 if( is->hThread != NULL ) {
\r
9799 ResumeThread( is->hThread );
\r
9802 if( is2 != NULL && is2->hThread != NULL ) {
\r
9803 ResumeThread( is2->hThread );
\r
9807 return (InputSourceRef) is;
\r
9811 RemoveInputSource(InputSourceRef isr)
\r
9815 is = (InputSource *) isr;
\r
9816 is->hThread = NULL; /* tell thread to stop */
\r
9817 CloseHandle(is->hThread);
\r
9818 if (is->second != NULL) {
\r
9819 is->second->hThread = NULL;
\r
9820 CloseHandle(is->second->hThread);
\r
9824 int no_wrap(char *message, int count)
\r
9826 ConsoleOutput(message, count, FALSE);
\r
9831 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9834 int outCount = SOCKET_ERROR;
\r
9835 ChildProc *cp = (ChildProc *) pr;
\r
9836 static OVERLAPPED ovl;
\r
9838 static int line = 0;
\r
9842 if (appData.noJoin || !appData.useInternalWrap)
\r
9843 return no_wrap(message, count);
\r
9846 int width = get_term_width();
\r
9847 int len = wrap(NULL, message, count, width, &line);
\r
9848 char *msg = malloc(len);
\r
9852 return no_wrap(message, count);
\r
9855 dbgchk = wrap(msg, message, count, width, &line);
\r
9856 if (dbgchk != len && appData.debugMode)
\r
9857 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9858 ConsoleOutput(msg, len, FALSE);
\r
9865 if (ovl.hEvent == NULL) {
\r
9866 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9868 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9870 switch (cp->kind) {
\r
9873 outCount = send(cp->sock, message, count, 0);
\r
9874 if (outCount == SOCKET_ERROR) {
\r
9875 *outError = WSAGetLastError();
\r
9877 *outError = NO_ERROR;
\r
9882 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9883 &dOutCount, NULL)) {
\r
9884 *outError = NO_ERROR;
\r
9885 outCount = (int) dOutCount;
\r
9887 *outError = GetLastError();
\r
9892 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9893 &dOutCount, &ovl);
\r
9894 if (*outError == NO_ERROR) {
\r
9895 outCount = (int) dOutCount;
\r
9905 if(n != 0) Sleep(n);
\r
9909 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9912 /* Ignore delay, not implemented for WinBoard */
\r
9913 return OutputToProcess(pr, message, count, outError);
\r
9918 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9919 char *buf, int count, int error)
\r
9921 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9924 /* see wgamelist.c for Game List functions */
\r
9925 /* see wedittags.c for Edit Tags functions */
\r
9932 char buf[MSG_SIZ];
\r
9935 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9936 f = fopen(buf, "r");
\r
9938 ProcessICSInitScript(f);
\r
9948 StartAnalysisClock()
\r
9950 if (analysisTimerEvent) return;
\r
9951 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9952 (UINT) 2000, NULL);
\r
9956 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9958 highlightInfo.sq[0].x = fromX;
\r
9959 highlightInfo.sq[0].y = fromY;
\r
9960 highlightInfo.sq[1].x = toX;
\r
9961 highlightInfo.sq[1].y = toY;
\r
9967 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9968 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9972 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9974 premoveHighlightInfo.sq[0].x = fromX;
\r
9975 premoveHighlightInfo.sq[0].y = fromY;
\r
9976 premoveHighlightInfo.sq[1].x = toX;
\r
9977 premoveHighlightInfo.sq[1].y = toY;
\r
9981 ClearPremoveHighlights()
\r
9983 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9984 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9988 ShutDownFrontEnd()
\r
9990 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9991 DeleteClipboardTempFiles();
\r
9997 if (IsIconic(hwndMain))
\r
9998 ShowWindow(hwndMain, SW_RESTORE);
\r
10000 SetActiveWindow(hwndMain);
\r
10004 * Prototypes for animation support routines
\r
10006 static void ScreenSquare(int column, int row, POINT * pt);
\r
10007 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
10008 POINT frames[], int * nFrames);
\r
10011 #define kFactor 4
\r
10014 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
10015 { // [HGM] atomic: animate blast wave
\r
10018 explodeInfo.fromX = fromX;
\r
10019 explodeInfo.fromY = fromY;
\r
10020 explodeInfo.toX = toX;
\r
10021 explodeInfo.toY = toY;
\r
10022 for(i=1; i<4*kFactor; i++) {
\r
10023 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
10024 DrawPosition(FALSE, board);
\r
10025 Sleep(appData.animSpeed);
\r
10027 explodeInfo.radius = 0;
\r
10028 DrawPosition(TRUE, board);
\r
10032 AnimateMove(board, fromX, fromY, toX, toY)
\r
10039 ChessSquare piece;
\r
10040 int x = toX, y = toY;
\r
10041 POINT start, finish, mid;
\r
10042 POINT frames[kFactor * 2 + 1];
\r
10045 if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();
\r
10047 if (!appData.animate) return;
\r
10048 if (doingSizing) return;
\r
10049 if (fromY < 0 || fromX < 0) return;
\r
10050 piece = board[fromY][fromX];
\r
10051 if (piece >= EmptySquare) return;
\r
10053 if(killX >= 0) toX = killX, toY = killY; // [HGM] lion: first to kill square
\r
10057 ScreenSquare(fromX, fromY, &start);
\r
10058 ScreenSquare(toX, toY, &finish);
\r
10060 /* All moves except knight jumps move in straight line */
\r
10061 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
10062 mid.x = start.x + (finish.x - start.x) / 2;
\r
10063 mid.y = start.y + (finish.y - start.y) / 2;
\r
10065 /* Knight: make straight movement then diagonal */
\r
10066 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
10067 mid.x = start.x + (finish.x - start.x) / 2;
\r
10071 mid.y = start.y + (finish.y - start.y) / 2;
\r
10075 /* Don't use as many frames for very short moves */
\r
10076 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
10077 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
10079 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
10081 animInfo.from.x = fromX;
\r
10082 animInfo.from.y = fromY;
\r
10083 animInfo.to.x = toX;
\r
10084 animInfo.to.y = toY;
\r
10085 animInfo.lastpos = start;
\r
10086 animInfo.piece = piece;
\r
10087 for (n = 0; n < nFrames; n++) {
\r
10088 animInfo.pos = frames[n];
\r
10089 DrawPosition(FALSE, NULL);
\r
10090 animInfo.lastpos = animInfo.pos;
\r
10091 Sleep(appData.animSpeed);
\r
10093 animInfo.pos = finish;
\r
10094 DrawPosition(FALSE, NULL);
\r
10096 if(toX != x || toY != y) { fromX = toX; fromY = toY; toX = x; toY = y; goto again; } // second leg
\r
10098 animInfo.piece = EmptySquare;
\r
10099 Explode(board, fromX, fromY, toX, toY);
\r
10102 /* Convert board position to corner of screen rect and color */
\r
10105 ScreenSquare(column, row, pt)
\r
10106 int column; int row; POINT * pt;
\r
10109 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
10110 pt->y = lineGap + row * (squareSize + lineGap) + border;
\r
10112 pt->x = lineGap + column * (squareSize + lineGap) + border;
\r
10113 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
10117 /* Generate a series of frame coords from start->mid->finish.
\r
10118 The movement rate doubles until the half way point is
\r
10119 reached, then halves back down to the final destination,
\r
10120 which gives a nice slow in/out effect. The algorithmn
\r
10121 may seem to generate too many intermediates for short
\r
10122 moves, but remember that the purpose is to attract the
\r
10123 viewers attention to the piece about to be moved and
\r
10124 then to where it ends up. Too few frames would be less
\r
10128 Tween(start, mid, finish, factor, frames, nFrames)
\r
10129 POINT * start; POINT * mid;
\r
10130 POINT * finish; int factor;
\r
10131 POINT frames[]; int * nFrames;
\r
10133 int n, fraction = 1, count = 0;
\r
10135 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
10136 for (n = 0; n < factor; n++)
\r
10138 for (n = 0; n < factor; n++) {
\r
10139 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
10140 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
10142 fraction = fraction / 2;
\r
10146 frames[count] = *mid;
\r
10149 /* Slow out, stepping 1/2, then 1/4, ... */
\r
10151 for (n = 0; n < factor; n++) {
\r
10152 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
10153 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
10155 fraction = fraction * 2;
\r
10157 *nFrames = count;
\r
10161 SettingsPopUp(ChessProgramState *cps)
\r
10162 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
10163 EngineOptionsPopup(savedHwnd, cps);
\r
10166 int flock(int fid, int code)
\r
10168 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
10170 ov.hEvent = NULL;
\r
10172 ov.OffsetHigh = 0;
\r
10174 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
10176 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
10177 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
10178 default: return -1;
\r
10187 static char col[8][20];
\r
10188 COLORREF color = *(COLORREF *) colorVariable[n];
\r
10190 snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
10195 ActivateTheme (int new)
\r
10196 { // Redo initialization of features depending on options that can occur in themes
\r
10198 if(new) InitDrawingColors();
\r
10199 fontBitmapSquareSize = 0; // request creation of new font pieces
\r
10200 InitDrawingSizes(boardSize, 0);
\r
10201 InvalidateRect(hwndMain, NULL, TRUE);
\r