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, 2016 Free
\r
9 * Software Foundation, Inc.
\r
11 * Enhancements Copyright 2005 Alessandro Scotti
\r
13 * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,
\r
14 * which was written and is copyrighted by Wayne Christopher.
\r
16 * The following terms apply to Digital Equipment Corporation's copyright
\r
17 * interest in XBoard:
\r
18 * ------------------------------------------------------------------------
\r
19 * All Rights Reserved
\r
21 * Permission to use, copy, modify, and distribute this software and its
\r
22 * documentation for any purpose and without fee is hereby granted,
\r
23 * provided that the above copyright notice appear in all copies and that
\r
24 * both that copyright notice and this permission notice appear in
\r
25 * supporting documentation, and that the name of Digital not be
\r
26 * used in advertising or publicity pertaining to distribution of the
\r
27 * software without specific, written prior permission.
\r
29 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
\r
30 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
\r
31 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
\r
32 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
\r
33 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
\r
34 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
\r
36 * ------------------------------------------------------------------------
\r
38 * The following terms apply to the enhanced version of XBoard
\r
39 * distributed by the Free Software Foundation:
\r
40 * ------------------------------------------------------------------------
\r
42 * GNU XBoard is free software: you can redistribute it and/or modify
\r
43 * it under the terms of the GNU General Public License as published by
\r
44 * the Free Software Foundation, either version 3 of the License, or (at
\r
45 * your option) any later version.
\r
47 * GNU XBoard is distributed in the hope that it will be useful, but
\r
48 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
49 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
50 * General Public License for more details.
\r
52 * You should have received a copy of the GNU General Public License
\r
53 * along with this program. If not, see http://www.gnu.org/licenses/. *
\r
55 *------------------------------------------------------------------------
\r
56 ** See the file ChangeLog for a revision history. */
\r
60 #include <windows.h>
\r
61 #include <winuser.h>
\r
62 #include <winsock.h>
\r
63 #include <commctrl.h>
\r
69 #include <sys/stat.h>
\r
72 #include <commdlg.h>
\r
74 #include <richedit.h>
\r
75 #include <mmsystem.h>
\r
85 #include "frontend.h"
\r
86 #include "backend.h"
\r
87 #include "winboard.h"
\r
89 #include "wclipbrd.h"
\r
90 #include "woptions.h"
\r
91 #include "wsockerr.h"
\r
92 #include "defaults.h"
\r
97 #define DATADIR "~~"
\r
99 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
\r
101 int myrandom(void);
\r
102 void mysrandom(unsigned int seed);
\r
104 extern int whiteFlag, blackFlag;
\r
105 Boolean flipClock = FALSE;
\r
106 extern HANDLE chatHandle[];
\r
107 extern enum ICS_TYPE ics_type;
\r
109 int MySearchPath P((char *installDir, char *name, char *fullname));
\r
110 int MyGetFullPathName P((char *name, char *fullname));
\r
111 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
112 VOID NewVariantPopup(HWND hwnd);
\r
113 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
114 /*char*/int promoChar));
\r
115 void DisplayMove P((int moveNumber));
\r
116 void ChatPopUp P((char *s));
\r
118 ChessSquare piece;
\r
119 POINT pos; /* window coordinates of current pos */
\r
120 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
121 POINT from; /* board coordinates of the piece's orig pos */
\r
122 POINT to; /* board coordinates of the piece's new pos */
\r
125 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
128 POINT start; /* window coordinates of start pos */
\r
129 POINT pos; /* window coordinates of current pos */
\r
130 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
131 POINT from; /* board coordinates of the piece's orig pos */
\r
135 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };
\r
138 POINT sq[2]; /* board coordinates of from, to squares */
\r
141 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
142 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
143 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
144 static HighlightInfo oldPartnerHighlight = { {{-1, -1}, {-1, -1}} };
\r
146 typedef struct { // [HGM] atomic
\r
147 int fromX, fromY, toX, toY, radius;
\r
150 static ExplodeInfo explodeInfo;
\r
152 /* Window class names */
\r
153 char szAppName[] = "WinBoard";
\r
154 char szConsoleName[] = "WBConsole";
\r
156 /* Title bar text */
\r
157 char szTitle[] = "WinBoard";
\r
158 char szConsoleTitle[] = "I C S Interaction";
\r
161 char *settingsFileName;
\r
162 Boolean saveSettingsOnExit;
\r
163 char installDir[MSG_SIZ];
\r
164 int errorExitStatus;
\r
166 BoardSize boardSize;
\r
167 Boolean chessProgram;
\r
168 //static int boardX, boardY;
\r
169 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
170 int squareSize, lineGap, minorSize;
\r
171 static int winW, winH;
\r
172 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
173 static int logoHeight = 0;
\r
174 static char messageText[MESSAGE_TEXT_MAX];
\r
175 static int clockTimerEvent = 0;
\r
176 static int loadGameTimerEvent = 0;
\r
177 static int analysisTimerEvent = 0;
\r
178 static DelayedEventCallback delayedTimerCallback;
\r
179 static int delayedTimerEvent = 0;
\r
180 static int buttonCount = 2;
\r
181 char *icsTextMenuString;
\r
183 char *firstChessProgramNames;
\r
184 char *secondChessProgramNames;
\r
186 #define PALETTESIZE 256
\r
188 HINSTANCE hInst; /* current instance */
\r
189 Boolean alwaysOnTop = FALSE;
\r
191 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
192 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
193 COLORREF markerColor[8] = { 0x00FFFF, 0x0000FF, 0x00FF00, 0xFF0000, 0xFFFF00, 0xFF00FF, 0xFFFFFF, 0x000000 };
\r
195 ColorClass currentColorClass;
\r
197 static HWND savedHwnd;
\r
198 HWND hCommPort = NULL; /* currently open comm port */
\r
199 static HWND hwndPause; /* pause button */
\r
200 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
201 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
202 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
203 explodeBrush, /* [HGM] atomic */
\r
204 markerBrush[8], /* [HGM] markers */
\r
205 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
206 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
207 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
208 static HPEN gridPen = NULL;
\r
209 static HPEN highlightPen = NULL;
\r
210 static HPEN premovePen = NULL;
\r
211 static NPLOGPALETTE pLogPal;
\r
212 static BOOL paletteChanged = FALSE;
\r
213 static HICON iconWhite, iconBlack, iconCurrent;
\r
214 static int doingSizing = FALSE;
\r
215 static int lastSizing = 0;
\r
216 static int prevStderrPort;
\r
217 static HBITMAP userLogo;
\r
219 static HBITMAP liteBackTexture = NULL;
\r
220 static HBITMAP darkBackTexture = NULL;
\r
221 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
222 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
223 static int backTextureSquareSize = 0;
\r
224 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
226 #if __GNUC__ && !defined(_winmajor)
\r
227 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
230 #if defined(_winmajor)
\r
231 #define oldDialog (_winmajor < 4)
\r
233 #define oldDialog 0
\r
237 #define INTERNATIONAL
\r
239 #ifdef INTERNATIONAL
\r
240 # define _(s) T_(s)
\r
246 # define Translate(x, y)
\r
247 # define LoadLanguageFile(s)
\r
250 #ifdef INTERNATIONAL
\r
252 Boolean barbaric; // flag indicating if translation is needed
\r
254 // list of item numbers used in each dialog (used to alter language at run time)
\r
256 #define ABOUTBOX -1 /* not sure why these are needed */
\r
257 #define ABOUTBOX2 -1
\r
259 int dialogItems[][42] = {
\r
260 { ABOUTBOX, IDOK, OPT_MESS, 400 },
\r
261 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed,
\r
262 OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors, IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL },
\r
263 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,
\r
264 OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds,
\r
265 OPT_Ranget, IDOK, IDCANCEL },
\r
266 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
267 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
268 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
269 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
270 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
271 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
272 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
273 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
274 { ABOUTBOX2, IDC_ChessBoard },
\r
275 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
276 OPT_GameListClose, IDC_GameListDoFilter },
\r
277 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
278 { DLG_Error, IDOK },
\r
279 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
280 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
281 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
282 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
283 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
284 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
285 { DLG_IndexNumber, IDC_Index },
\r
286 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
287 { DLG_TypeInName, IDOK, IDCANCEL },
\r
288 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
289 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
290 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
291 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
292 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
293 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
294 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
295 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
296 OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove },
\r
297 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
298 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
299 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
300 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
301 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
302 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
303 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
304 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
305 GPB_General, GPB_Alarm, OPT_AutoCreate },
\r
306 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
307 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
308 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
309 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
310 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
311 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
312 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
313 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid },
\r
314 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
315 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
316 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
317 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
318 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
319 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
320 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,
\r
321 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
322 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
323 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
324 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
325 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,
\r
326 OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7,
\r
327 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
328 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
329 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
330 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
331 { DLG_MoveHistory },
\r
332 { DLG_EvalGraph },
\r
333 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
334 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
335 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
336 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
337 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
338 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
339 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
340 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
341 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
345 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];
\r
346 static int lastChecked;
\r
347 static char oldLanguage[MSG_SIZ], *menuText[10][30];
\r
348 extern int tinyLayout;
\r
349 extern char * menuBarText[][10];
\r
352 LoadLanguageFile(char *name)
\r
353 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
355 int i=0, j=0, n=0, k;
\r
358 if(!name || name[0] == NULLCHAR) return;
\r
359 snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension
\r
360 appData.language = oldLanguage;
\r
361 if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on
\r
362 if((f = fopen(buf, "r")) == NULL) return;
\r
363 while((k = fgetc(f)) != EOF) {
\r
364 if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }
\r
365 languageBuf[i] = k;
\r
367 if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {
\r
369 if(p = strstr(languageBuf + n + 1, "\" === \"")) {
\r
370 if(p > languageBuf+n+2 && p+8 < languageBuf+i) {
\r
371 if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }
\r
372 english[j] = languageBuf + n + 1; *p = 0;
\r
373 foreign[j++] = p + 7; languageBuf[i-1] = 0;
\r
374 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);
\r
379 } else if(i > 0 && languageBuf[i-1] == '\\') {
\r
381 case 'n': k = '\n'; break;
\r
382 case 'r': k = '\r'; break;
\r
383 case 't': k = '\t'; break;
\r
385 languageBuf[--i] = k;
\r
390 barbaric = (j != 0);
\r
391 safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );
\r
396 { // return the translation of the given string
\r
397 // efficiency can be improved a lot...
\r
399 static char buf[MSG_SIZ];
\r
400 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
401 if(!barbaric) return s;
\r
402 if(!s) return ""; // sanity
\r
403 while(english[i]) {
\r
404 if(!strcmp(s, english[i])) return foreign[i];
\r
405 if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending
\r
406 snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion
\r
415 Translate(HWND hDlg, int dialogID)
\r
416 { // translate all text items in the given dialog
\r
418 char buf[MSG_SIZ], *s;
\r
419 if(!barbaric) return;
\r
420 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
421 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
422 GetWindowText( hDlg, buf, MSG_SIZ );
\r
424 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
425 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
426 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
427 if(strlen(buf) == 0) continue;
\r
429 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
434 TranslateOneMenu(int i, HMENU subMenu)
\r
437 static MENUITEMINFO info;
\r
439 info.cbSize = sizeof(MENUITEMINFO);
\r
440 info.fMask = MIIM_STATE | MIIM_TYPE;
\r
441 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
443 info.dwTypeData = buf;
\r
444 info.cch = sizeof(buf);
\r
445 GetMenuItemInfo(subMenu, j, TRUE, &info);
\r
447 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );
\r
448 else menuText[i][j] = strdup(buf); // remember original on first change
\r
450 if(buf[0] == NULLCHAR) continue;
\r
451 info.dwTypeData = T_(buf);
\r
452 info.cch = strlen(buf)+1;
\r
453 SetMenuItemInfo(subMenu, j, TRUE, &info);
\r
459 TranslateMenus(int addLanguage)
\r
462 WIN32_FIND_DATA fileData;
\r
464 #define IDM_English 1970
\r
466 HMENU mainMenu = GetMenu(hwndMain);
\r
467 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
468 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
469 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
470 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
471 TranslateOneMenu(i, subMenu);
\r
473 DrawMenuBar(hwndMain);
\r
476 if(!addLanguage) return;
\r
477 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
478 HMENU mainMenu = GetMenu(hwndMain);
\r
479 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
480 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
481 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
482 i = 0; lastChecked = IDM_English;
\r
484 char *p, *q = fileData.cFileName;
\r
485 int checkFlag = MF_UNCHECKED;
\r
486 languageFile[i] = strdup(q);
\r
487 if(barbaric && !strcmp(oldLanguage, q)) {
\r
488 checkFlag = MF_CHECKED;
\r
489 lastChecked = IDM_English + i + 1;
\r
490 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
492 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
493 p = strstr(fileData.cFileName, ".lng");
\r
495 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
496 } while(FindNextFile(hFind, &fileData));
\r
503 #define IDM_RecentEngines 3000
\r
506 RecentEngineMenu (char *s)
\r
508 if(appData.icsActive) return;
\r
509 if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty
\r
510 HMENU mainMenu = GetMenu(hwndMain);
\r
511 HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu
\r
512 int i=IDM_RecentEngines;
\r
513 recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu
\r
514 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
516 char *p = strchr(s, '\n');
\r
517 if(p == NULL) return; // malformed!
\r
519 AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);
\r
533 int cliWidth, cliHeight;
\r
536 SizeInfo sizeInfo[] =
\r
538 { "tiny", 21, 0, 1, 2, 0, 0 },
\r
539 { "teeny", 25, 1, 1, 2, 0, 0 },
\r
540 { "dinky", 29, 1, 1, 2, 0, 0 },
\r
541 { "petite", 33, 1, 1, 2, 0, 0 },
\r
542 { "slim", 37, 2, 1, 1, 0, 0 },
\r
543 { "small", 40, 2, 1, 1, 0, 0 },
\r
544 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
545 { "middling", 49, 2, 0, 0, 0, 0 },
\r
546 { "average", 54, 2, 0, 0, 0, 0 },
\r
547 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
548 { "medium", 64, 3, 0, 0, 0, 0 },
\r
549 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
550 { "large", 80, 3, 0, 0, 0, 0 },
\r
551 { "big", 87, 3, 0, 0, 0, 0 },
\r
552 { "huge", 95, 3, 0, 0, 0, 0 },
\r
553 { "giant", 108, 3, 0, 0, 0, 0 },
\r
554 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
555 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
556 { NULL, 0, 0, 0, 0, 0, 0 }
\r
559 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
560 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
562 { 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
563 { 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
564 { 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
565 { 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
566 { 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
567 { 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
568 { 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
569 { 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
570 { 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
571 { 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
572 { 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
573 { 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
574 { 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
575 { 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
576 { 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
577 { 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
578 { 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
579 { 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
582 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
591 #define BUTTON_WIDTH (tinyLayout == 2 ? 16 : 32)
\r
592 #define N_BUTTONS 5
\r
594 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
596 {"<<", IDM_ToStart, NULL, NULL},
\r
597 {"<", IDM_Backward, NULL, NULL},
\r
598 {"P", IDM_Pause, NULL, NULL},
\r
599 {">", IDM_Forward, NULL, NULL},
\r
600 {">>", IDM_ToEnd, NULL, NULL},
\r
603 int tinyLayout = 0, smallLayout = 0;
\r
604 #define MENU_BAR_ITEMS 9
\r
605 char *menuBarText[3][MENU_BAR_ITEMS+1] = {
\r
606 { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },
\r
607 { N_("&Fil"), N_("&Ed"), N_("&Vw"), N_("&Mod"), N_("&Act"), N_("E&ng"), N_("&Opt"), N_("&Hlp"), NULL },
\r
608 { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },
\r
612 MySound sounds[(int)NSoundClasses];
\r
613 MyTextAttribs textAttribs[(int)NColorClasses];
\r
615 MyColorizeAttribs colorizeAttribs[] = {
\r
616 { (COLORREF)0, 0, N_("Shout Text") },
\r
617 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
618 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
619 { (COLORREF)0, 0, N_("Channel Text") },
\r
620 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
621 { (COLORREF)0, 0, N_("Tell Text") },
\r
622 { (COLORREF)0, 0, N_("Challenge Text") },
\r
623 { (COLORREF)0, 0, N_("Request Text") },
\r
624 { (COLORREF)0, 0, N_("Seek Text") },
\r
625 { (COLORREF)0, 0, N_("Normal Text") },
\r
626 { (COLORREF)0, 0, N_("None") }
\r
631 static char *commentTitle;
\r
632 static char *commentText;
\r
633 static int commentIndex;
\r
634 static Boolean editComment = FALSE;
\r
637 char errorTitle[MSG_SIZ];
\r
638 char errorMessage[2*MSG_SIZ];
\r
639 HWND errorDialog = NULL;
\r
640 BOOLEAN moveErrorMessageUp = FALSE;
\r
641 BOOLEAN consoleEcho = TRUE;
\r
642 CHARFORMAT consoleCF;
\r
643 COLORREF consoleBackgroundColor;
\r
645 char *programVersion;
\r
651 typedef int CPKind;
\r
660 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
663 #define INPUT_SOURCE_BUF_SIZE 4096
\r
665 typedef struct _InputSource {
\r
672 char buf[INPUT_SOURCE_BUF_SIZE];
\r
676 InputCallback func;
\r
677 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
681 InputSource *consoleInputSource;
\r
686 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
687 VOID ConsoleCreate();
\r
689 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
690 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
691 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
692 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
694 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
695 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
696 void ParseIcsTextMenu(char *icsTextMenuString);
\r
697 VOID PopUpNameDialog(char firstchar);
\r
698 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
702 int GameListOptions();
\r
704 int dummy; // [HGM] for obsolete args
\r
706 HWND hwndMain = NULL; /* root window*/
\r
707 HWND hwndConsole = NULL;
\r
708 HWND commentDialog = NULL;
\r
709 HWND moveHistoryDialog = NULL;
\r
710 HWND evalGraphDialog = NULL;
\r
711 HWND engineOutputDialog = NULL;
\r
712 HWND gameListDialog = NULL;
\r
713 HWND editTagsDialog = NULL;
\r
715 int commentUp = FALSE;
\r
717 WindowPlacement wpMain;
\r
718 WindowPlacement wpConsole;
\r
719 WindowPlacement wpComment;
\r
720 WindowPlacement wpMoveHistory;
\r
721 WindowPlacement wpEvalGraph;
\r
722 WindowPlacement wpEngineOutput;
\r
723 WindowPlacement wpGameList;
\r
724 WindowPlacement wpTags;
\r
726 VOID EngineOptionsPopup(); // [HGM] settings
\r
728 VOID GothicPopUp(char *title, VariantClass variant);
\r
730 * Setting "frozen" should disable all user input other than deleting
\r
731 * the window. We do this while engines are initializing themselves.
\r
733 static int frozen = 0;
\r
734 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
740 if (frozen) return;
\r
742 hmenu = GetMenu(hwndMain);
\r
743 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
744 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
746 DrawMenuBar(hwndMain);
\r
749 /* Undo a FreezeUI */
\r
755 if (!frozen) return;
\r
757 hmenu = GetMenu(hwndMain);
\r
758 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
759 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
761 DrawMenuBar(hwndMain);
\r
764 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
766 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
772 #define JAWS_ALT_INTERCEPT
\r
773 #define JAWS_KBUP_NAVIGATION
\r
774 #define JAWS_KBDOWN_NAVIGATION
\r
775 #define JAWS_MENU_ITEMS
\r
776 #define JAWS_SILENCE
\r
777 #define JAWS_REPLAY
\r
779 #define JAWS_COPYRIGHT
\r
780 #define JAWS_DELETE(X) X
\r
781 #define SAYMACHINEMOVE()
\r
785 /*---------------------------------------------------------------------------*\
\r
789 \*---------------------------------------------------------------------------*/
\r
791 static void HandleMessage P((MSG *message));
\r
792 static HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
795 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
796 LPSTR lpCmdLine, int nCmdShow)
\r
799 // INITCOMMONCONTROLSEX ex;
\r
803 LoadLibrary("RICHED32.DLL");
\r
804 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
806 if (!InitApplication(hInstance)) {
\r
809 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
816 // InitCommonControlsEx(&ex);
\r
817 InitCommonControls();
\r
819 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
820 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
821 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
823 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
825 while (GetMessage(&msg, /* message structure */
\r
826 NULL, /* handle of window receiving the message */
\r
827 0, /* lowest message to examine */
\r
828 0)) /* highest message to examine */
\r
830 HandleMessage(&msg);
\r
834 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
838 HandleMessage (MSG *message)
\r
840 MSG msg = *message;
\r
842 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
843 // [HGM] navigate: switch between all windows with tab
\r
844 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
845 int i, currentElement = 0;
\r
847 // first determine what element of the chain we come from (if any)
\r
848 if(appData.icsActive) {
\r
849 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
850 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
852 if(engineOutputDialog && EngineOutputIsUp()) {
\r
853 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
854 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
856 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
857 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
859 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
860 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
861 if(msg.hwnd == e1) currentElement = 2; else
\r
862 if(msg.hwnd == e2) currentElement = 3; else
\r
863 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
864 if(msg.hwnd == mh) currentElement = 4; else
\r
865 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
866 if(msg.hwnd == hText) currentElement = 5; else
\r
867 if(msg.hwnd == hInput) currentElement = 6; else
\r
868 for (i = 0; i < N_BUTTONS; i++) {
\r
869 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
872 // determine where to go to
\r
873 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
875 currentElement = (currentElement + direction) % 7;
\r
876 switch(currentElement) {
\r
878 h = hwndMain; break; // passing this case always makes the loop exit
\r
880 h = buttonDesc[0].hwnd; break; // could be NULL
\r
882 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
885 if(!EngineOutputIsUp()) continue;
\r
888 if(!MoveHistoryIsUp()) continue;
\r
890 // case 6: // input to eval graph does not seem to get here!
\r
891 // if(!EvalGraphIsUp()) continue;
\r
892 // h = evalGraphDialog; break;
\r
894 if(!appData.icsActive) continue;
\r
898 if(!appData.icsActive) continue;
\r
904 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
905 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
908 return; // this message now has been processed
\r
912 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
913 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
914 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
915 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
916 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
917 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
918 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
919 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
920 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
921 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
922 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
923 for(i=0; i<MAX_CHAT; i++)
\r
924 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
927 if(done) return; // [HGM] chat: end patch
\r
928 TranslateMessage(&msg); /* Translates virtual key codes */
\r
929 DispatchMessage(&msg); /* Dispatches message to window */
\r
935 { /* Dispatch pending messages */
\r
937 while (PeekMessage(&msg, /* message structure */
\r
938 NULL, /* handle of window receiving the message */
\r
939 0, /* lowest message to examine */
\r
940 0, /* highest message to examine */
\r
943 HandleMessage(&msg);
\r
947 /*---------------------------------------------------------------------------*\
\r
949 * Initialization functions
\r
951 \*---------------------------------------------------------------------------*/
\r
955 { // update user logo if necessary
\r
956 static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;
\r
958 if(appData.autoLogo) {
\r
959 curName = UserName();
\r
960 if(strcmp(curName, oldUserName)) {
\r
961 GetCurrentDirectory(MSG_SIZ, dir);
\r
962 SetCurrentDirectory(installDir);
\r
963 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
964 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
965 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
966 if(userLogo == NULL)
\r
967 userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
968 SetCurrentDirectory(dir); /* return to prev directory */
\r
974 InitApplication(HINSTANCE hInstance)
\r
978 /* Fill in window class structure with parameters that describe the */
\r
981 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
982 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
983 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
984 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
985 wc.hInstance = hInstance; /* Owner of this class */
\r
986 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
987 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
988 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
989 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
990 wc.lpszClassName = szAppName; /* Name to register as */
\r
992 /* Register the window class and return success/failure code. */
\r
993 if (!RegisterClass(&wc)) return FALSE;
\r
995 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
996 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
998 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
999 wc.hInstance = hInstance;
\r
1000 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
1001 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
1002 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
1003 wc.lpszMenuName = NULL;
\r
1004 wc.lpszClassName = szConsoleName;
\r
1006 if (!RegisterClass(&wc)) return FALSE;
\r
1011 /* Set by InitInstance, used by EnsureOnScreen */
\r
1012 int screenHeight, screenWidth;
\r
1013 RECT screenGeometry;
\r
1016 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
1018 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
1019 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
1020 if (*x > screenGeometry.right - 32) *x = screenGeometry.left;
\r
1021 if (*y > screenGeometry.bottom - 32) *y = screenGeometry.top;
\r
1022 if (*x < screenGeometry.left + minX) *x = screenGeometry.left + minX;
\r
1023 if (*y < screenGeometry.top + minY) *y = screenGeometry.top + minY;
\r
1027 LoadLogo(ChessProgramState *cps, int n, Boolean ics)
\r
1029 char buf[MSG_SIZ], dir[MSG_SIZ];
\r
1030 GetCurrentDirectory(MSG_SIZ, dir);
\r
1031 SetCurrentDirectory(installDir);
\r
1032 if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {
\r
1033 cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1035 if (cps->programLogo == NULL && appData.debugMode) {
\r
1036 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );
\r
1038 } else if(appData.autoLogo) {
\r
1039 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
\r
1040 char *opponent = "";
\r
1041 if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;
\r
1042 if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;
\r
1043 sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);
\r
1044 if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {
\r
1045 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
1046 cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1049 if(appData.directory[n] && appData.directory[n][0]) {
\r
1050 SetCurrentDirectory(appData.directory[n]);
\r
1051 cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1054 SetCurrentDirectory(dir); /* return to prev directory */
\r
1060 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
1061 backTextureSquareSize = 0; // kludge to force recalculation of texturemode
\r
1063 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1064 if(liteBackTexture) DeleteObject(liteBackTexture);
\r
1065 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1066 liteBackTextureMode = appData.liteBackTextureMode;
\r
1068 if (liteBackTexture == NULL && appData.debugMode) {
\r
1069 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1073 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1074 if(darkBackTexture) DeleteObject(darkBackTexture);
\r
1075 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1076 darkBackTextureMode = appData.darkBackTextureMode;
\r
1078 if (darkBackTexture == NULL && appData.debugMode) {
\r
1079 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1084 #ifndef SM_CXVIRTUALSCREEN
\r
1085 #define SM_CXVIRTUALSCREEN 78
\r
1087 #ifndef SM_CYVIRTUALSCREEN
\r
1088 #define SM_CYVIRTUALSCREEN 79
\r
1090 #ifndef SM_XVIRTUALSCREEN
\r
1091 #define SM_XVIRTUALSCREEN 76
\r
1093 #ifndef SM_YVIRTUALSCREEN
\r
1094 #define SM_YVIRTUALSCREEN 77
\r
1100 screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
\r
1101 if( !screenHeight ) screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1102 screenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
\r
1103 if( !screenWidth ) screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1104 screenGeometry.left = GetSystemMetrics(SM_XVIRTUALSCREEN);
\r
1105 screenGeometry.top = GetSystemMetrics(SM_YVIRTUALSCREEN);
\r
1106 screenGeometry.right = screenGeometry.left + screenWidth;
\r
1107 screenGeometry.bottom = screenGeometry.top + screenHeight;
\r
1110 ChessProgramState broadcast;
\r
1113 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
1115 HWND hwnd; /* Main window handle. */
\r
1117 WINDOWPLACEMENT wp;
\r
1120 hInst = hInstance; /* Store instance handle in our global variable */
\r
1121 programName = szAppName;
\r
1123 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
1124 *filepart = NULLCHAR;
\r
1125 SetCurrentDirectory(installDir);
\r
1127 GetCurrentDirectory(MSG_SIZ, installDir);
\r
1129 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
1131 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
1132 /* xboard, and older WinBoards, controlled the move sound with the
\r
1133 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
1134 always turn the option on (so that the backend will call us),
\r
1135 then let the user turn the sound off by setting it to silence if
\r
1136 desired. To accommodate old winboard.ini files saved by old
\r
1137 versions of WinBoard, we also turn off the sound if the option
\r
1138 was initially set to false. [HGM] taken out of InitAppData */
\r
1139 if (!appData.ringBellAfterMoves) {
\r
1140 sounds[(int)SoundMove].name = strdup("");
\r
1141 appData.ringBellAfterMoves = TRUE;
\r
1143 if (appData.debugMode) {
\r
1144 char *c = appData.nameOfDebugFile;
\r
1145 if(strstr(c, "///") == c) {
\r
1146 broadcast.which = "broadcaster";
\r
1147 broadcast.pr = NoProc;
\r
1148 broadcast.isr = NULL;
\r
1149 broadcast.program = c + 3;
\r
1150 broadcast.dir = ".";
\r
1151 broadcast.host = "localhost";
\r
1152 StartChessProgram(&broadcast);
\r
1153 debugFP = (FILE*) _fdopen(_open_osfhandle((long)(((ChildProc*)(broadcast.pr))->hTo), _O_WRONLY), "w");
\r
1155 debugFP = fopen(c, "w");
\r
1156 setbuf(debugFP, NULL);
\r
1159 LoadLanguageFile(appData.language);
\r
1163 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
1164 // InitEngineUCI( installDir, &second );
\r
1166 /* Create a main window for this application instance. */
\r
1167 hwnd = CreateWindow(szAppName, szTitle,
\r
1168 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
1169 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
1170 NULL, NULL, hInstance, NULL);
\r
1173 /* If window could not be created, return "failure" */
\r
1178 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1179 LoadLogo(&first, 0, FALSE);
\r
1180 LoadLogo(&second, 1, appData.icsActive);
\r
1184 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1185 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1186 iconCurrent = iconWhite;
\r
1187 InitDrawingColors();
\r
1189 InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args
\r
1190 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1191 /* Compute window size for each board size, and use the largest
\r
1192 size that fits on this screen as the default. */
\r
1193 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1194 if (boardSize == (BoardSize)-1 &&
\r
1195 winH <= screenHeight
\r
1196 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1197 && winW <= screenWidth) {
\r
1198 boardSize = (BoardSize)ibs;
\r
1202 InitDrawingSizes(boardSize, 0);
\r
1203 RecentEngineMenu(appData.recentEngineList);
\r
1205 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1207 /* [AS] Load textures if specified */
\r
1210 mysrandom( (unsigned) time(NULL) );
\r
1212 /* [AS] Restore layout */
\r
1213 if( wpMoveHistory.visible ) {
\r
1214 MoveHistoryPopUp();
\r
1217 if( wpEvalGraph.visible ) {
\r
1221 if( wpEngineOutput.visible ) {
\r
1222 EngineOutputPopUp();
\r
1225 /* Make the window visible; update its client area; and return "success" */
\r
1226 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1227 wp.length = sizeof(WINDOWPLACEMENT);
\r
1229 wp.showCmd = nCmdShow;
\r
1230 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1231 wp.rcNormalPosition.left = wpMain.x;
\r
1232 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1233 wp.rcNormalPosition.top = wpMain.y;
\r
1234 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1235 SetWindowPlacement(hwndMain, &wp);
\r
1237 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1239 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1240 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1242 if (hwndConsole) {
\r
1244 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1245 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1247 ShowWindow(hwndConsole, nCmdShow);
\r
1248 SetActiveWindow(hwndConsole);
\r
1250 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1251 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1260 HMENU hmenu = GetMenu(hwndMain);
\r
1262 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1263 MF_BYCOMMAND|((appData.icsActive &&
\r
1264 *appData.icsCommPort != NULLCHAR) ?
\r
1265 MF_ENABLED : MF_GRAYED));
\r
1266 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1267 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1268 MF_CHECKED : MF_UNCHECKED));
\r
1269 EnableMenuItem(hmenu, IDM_SaveSelected, MF_GRAYED);
\r
1272 //---------------------------------------------------------------------------------------------------------
\r
1274 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1275 #define XBOARD FALSE
\r
1277 #define OPTCHAR "/"
\r
1278 #define SEPCHAR "="
\r
1279 #define TOPLEVEL 0
\r
1283 // front-end part of option handling
\r
1286 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1288 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1289 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1292 lf->lfEscapement = 0;
\r
1293 lf->lfOrientation = 0;
\r
1294 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1295 lf->lfItalic = mfp->italic;
\r
1296 lf->lfUnderline = mfp->underline;
\r
1297 lf->lfStrikeOut = mfp->strikeout;
\r
1298 lf->lfCharSet = mfp->charset;
\r
1299 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1303 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1304 lf->lfQuality = DEFAULT_QUALITY;
\r
1305 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1306 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1310 CreateFontInMF(MyFont *mf)
\r
1312 LFfromMFP(&mf->lf, &mf->mfp);
\r
1313 if (mf->hf) DeleteObject(mf->hf);
\r
1314 mf->hf = CreateFontIndirect(&mf->lf);
\r
1317 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1319 colorVariable[] = {
\r
1320 &whitePieceColor,
\r
1321 &blackPieceColor,
\r
1322 &lightSquareColor,
\r
1323 &darkSquareColor,
\r
1324 &highlightSquareColor,
\r
1325 &premoveHighlightColor,
\r
1327 &consoleBackgroundColor,
\r
1328 &appData.fontForeColorWhite,
\r
1329 &appData.fontBackColorWhite,
\r
1330 &appData.fontForeColorBlack,
\r
1331 &appData.fontBackColorBlack,
\r
1332 &appData.evalHistColorWhite,
\r
1333 &appData.evalHistColorBlack,
\r
1334 &appData.highlightArrowColor,
\r
1337 /* Command line font name parser. NULL name means do nothing.
\r
1338 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1339 For backward compatibility, syntax without the colon is also
\r
1340 accepted, but font names with digits in them won't work in that case.
\r
1343 ParseFontName(char *name, MyFontParams *mfp)
\r
1346 if (name == NULL) return;
\r
1348 q = strchr(p, ':');
\r
1350 if (q - p >= sizeof(mfp->faceName))
\r
1351 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1352 memcpy(mfp->faceName, p, q - p);
\r
1353 mfp->faceName[q - p] = NULLCHAR;
\r
1356 q = mfp->faceName;
\r
1358 while (*p && !isdigit(*p)) {
\r
1360 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1361 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1363 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1366 if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);
\r
1367 mfp->pointSize = (float) atof(p);
\r
1368 mfp->bold = (strchr(p, 'b') != NULL);
\r
1369 mfp->italic = (strchr(p, 'i') != NULL);
\r
1370 mfp->underline = (strchr(p, 'u') != NULL);
\r
1371 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1372 mfp->charset = DEFAULT_CHARSET;
\r
1373 q = strchr(p, 'c');
\r
1375 mfp->charset = (BYTE) atoi(q+1);
\r
1379 ParseFont(char *name, int number)
\r
1380 { // wrapper to shield back-end from 'font'
\r
1381 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1386 { // in WB we have a 2D array of fonts; this initializes their description
\r
1388 /* Point font array elements to structures and
\r
1389 parse default font names */
\r
1390 for (i=0; i<NUM_FONTS; i++) {
\r
1391 for (j=0; j<NUM_SIZES; j++) {
\r
1392 font[j][i] = &fontRec[j][i];
\r
1393 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1400 { // here we create the actual fonts from the selected descriptions
\r
1402 for (i=0; i<NUM_FONTS; i++) {
\r
1403 for (j=0; j<NUM_SIZES; j++) {
\r
1404 CreateFontInMF(font[j][i]);
\r
1408 /* Color name parser.
\r
1409 X version accepts X color names, but this one
\r
1410 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1412 ParseColorName(char *name)
\r
1414 int red, green, blue, count;
\r
1415 char buf[MSG_SIZ];
\r
1417 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1419 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1420 &red, &green, &blue);
\r
1423 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1424 DisplayError(buf, 0);
\r
1425 return RGB(0, 0, 0);
\r
1427 return PALETTERGB(red, green, blue);
\r
1431 ParseColor(int n, char *name)
\r
1432 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1433 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1437 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1439 char *e = argValue;
\r
1443 if (*e == 'b') eff |= CFE_BOLD;
\r
1444 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1445 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1446 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1447 else if (*e == '#' || isdigit(*e)) break;
\r
1451 *color = ParseColorName(e);
\r
1455 ParseTextAttribs(ColorClass cc, char *s)
\r
1456 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1457 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1458 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1462 ParseBoardSize(void *addr, char *name)
\r
1463 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1464 BoardSize bs = SizeTiny;
\r
1465 while (sizeInfo[bs].name != NULL) {
\r
1466 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1467 *(BoardSize *)addr = bs;
\r
1472 ExitArgError(_("Unrecognized board size value"), name, TRUE);
\r
1477 { // [HGM] import name from appData first
\r
1480 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1481 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1482 textAttribs[cc].sound.data = NULL;
\r
1483 MyLoadSound(&textAttribs[cc].sound);
\r
1485 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1486 textAttribs[cc].sound.name = strdup("");
\r
1487 textAttribs[cc].sound.data = NULL;
\r
1489 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1490 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1491 sounds[sc].data = NULL;
\r
1492 MyLoadSound(&sounds[sc]);
\r
1497 SetCommPortDefaults()
\r
1499 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1500 dcb.DCBlength = sizeof(DCB);
\r
1501 dcb.BaudRate = 9600;
\r
1502 dcb.fBinary = TRUE;
\r
1503 dcb.fParity = FALSE;
\r
1504 dcb.fOutxCtsFlow = FALSE;
\r
1505 dcb.fOutxDsrFlow = FALSE;
\r
1506 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1507 dcb.fDsrSensitivity = FALSE;
\r
1508 dcb.fTXContinueOnXoff = TRUE;
\r
1509 dcb.fOutX = FALSE;
\r
1511 dcb.fNull = FALSE;
\r
1512 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1513 dcb.fAbortOnError = FALSE;
\r
1515 dcb.Parity = SPACEPARITY;
\r
1516 dcb.StopBits = ONESTOPBIT;
\r
1519 // [HGM] args: these three cases taken out to stay in front-end
\r
1521 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1522 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1523 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1524 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1526 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1527 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1528 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1529 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1530 ad->argName, mfp->faceName, mfp->pointSize,
\r
1531 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1532 mfp->bold ? "b" : "",
\r
1533 mfp->italic ? "i" : "",
\r
1534 mfp->underline ? "u" : "",
\r
1535 mfp->strikeout ? "s" : "",
\r
1536 (int)mfp->charset);
\r
1542 { // [HGM] copy the names from the internal WB variables to appData
\r
1545 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1546 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1547 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1548 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1552 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1553 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1554 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1555 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1556 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1557 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1558 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1559 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1560 (ta->effects) ? " " : "",
\r
1561 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1565 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1566 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1567 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1568 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1569 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1573 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1574 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1575 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1579 ParseCommPortSettings(char *s)
\r
1580 { // wrapper to keep dcb from back-end
\r
1581 ParseCommSettings(s, &dcb);
\r
1586 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1587 GetActualPlacement(hwndMain, &wpMain);
\r
1588 GetActualPlacement(hwndConsole, &wpConsole);
\r
1589 GetActualPlacement(commentDialog, &wpComment);
\r
1590 GetActualPlacement(editTagsDialog, &wpTags);
\r
1591 GetActualPlacement(gameListDialog, &wpGameList);
\r
1592 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1593 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1594 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1598 PrintCommPortSettings(FILE *f, char *name)
\r
1599 { // wrapper to shield back-end from DCB
\r
1600 PrintCommSettings(f, name, &dcb);
\r
1604 MySearchPath(char *installDir, char *name, char *fullname)
\r
1606 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1607 if(name[0]== '%') {
\r
1608 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1609 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1610 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1611 *strchr(buf, '%') = 0;
\r
1612 strcat(fullname, getenv(buf));
\r
1613 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1615 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1616 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1617 return (int) strlen(fullname);
\r
1619 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1623 MyGetFullPathName(char *name, char *fullname)
\r
1626 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1631 { // [HGM] args: allows testing if main window is realized from back-end
\r
1632 return hwndMain != NULL;
\r
1636 PopUpStartupDialog()
\r
1640 LoadLanguageFile(appData.language);
\r
1641 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1642 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1643 FreeProcInstance(lpProc);
\r
1646 /*---------------------------------------------------------------------------*\
\r
1648 * GDI board drawing routines
\r
1650 \*---------------------------------------------------------------------------*/
\r
1652 /* [AS] Draw square using background texture */
\r
1653 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1658 return; /* Should never happen! */
\r
1661 SetGraphicsMode( dst, GM_ADVANCED );
\r
1668 /* X reflection */
\r
1673 x.eDx = (FLOAT) dw + dx - 1;
\r
1676 SetWorldTransform( dst, &x );
\r
1679 /* Y reflection */
\r
1685 x.eDy = (FLOAT) dh + dy - 1;
\r
1687 SetWorldTransform( dst, &x );
\r
1695 x.eDx = (FLOAT) dx;
\r
1696 x.eDy = (FLOAT) dy;
\r
1699 SetWorldTransform( dst, &x );
\r
1703 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1711 SetWorldTransform( dst, &x );
\r
1713 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1716 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1718 PM_WP = (int) WhitePawn,
\r
1719 PM_WN = (int) WhiteKnight,
\r
1720 PM_WB = (int) WhiteBishop,
\r
1721 PM_WR = (int) WhiteRook,
\r
1722 PM_WQ = (int) WhiteQueen,
\r
1723 PM_WF = (int) WhiteFerz,
\r
1724 PM_WW = (int) WhiteWazir,
\r
1725 PM_WE = (int) WhiteAlfil,
\r
1726 PM_WM = (int) WhiteMan,
\r
1727 PM_WO = (int) WhiteCannon,
\r
1728 PM_WU = (int) WhiteUnicorn,
\r
1729 PM_WH = (int) WhiteNightrider,
\r
1730 PM_WA = (int) WhiteAngel,
\r
1731 PM_WC = (int) WhiteMarshall,
\r
1732 PM_WAB = (int) WhiteCardinal,
\r
1733 PM_WD = (int) WhiteDragon,
\r
1734 PM_WL = (int) WhiteLance,
\r
1735 PM_WS = (int) WhiteCobra,
\r
1736 PM_WV = (int) WhiteFalcon,
\r
1737 PM_WSG = (int) WhiteSilver,
\r
1738 PM_WG = (int) WhiteGrasshopper,
\r
1739 PM_WK = (int) WhiteKing,
\r
1740 PM_BP = (int) BlackPawn,
\r
1741 PM_BN = (int) BlackKnight,
\r
1742 PM_BB = (int) BlackBishop,
\r
1743 PM_BR = (int) BlackRook,
\r
1744 PM_BQ = (int) BlackQueen,
\r
1745 PM_BF = (int) BlackFerz,
\r
1746 PM_BW = (int) BlackWazir,
\r
1747 PM_BE = (int) BlackAlfil,
\r
1748 PM_BM = (int) BlackMan,
\r
1749 PM_BO = (int) BlackCannon,
\r
1750 PM_BU = (int) BlackUnicorn,
\r
1751 PM_BH = (int) BlackNightrider,
\r
1752 PM_BA = (int) BlackAngel,
\r
1753 PM_BC = (int) BlackMarshall,
\r
1754 PM_BG = (int) BlackGrasshopper,
\r
1755 PM_BAB = (int) BlackCardinal,
\r
1756 PM_BD = (int) BlackDragon,
\r
1757 PM_BL = (int) BlackLance,
\r
1758 PM_BS = (int) BlackCobra,
\r
1759 PM_BV = (int) BlackFalcon,
\r
1760 PM_BSG = (int) BlackSilver,
\r
1761 PM_BK = (int) BlackKing
\r
1764 static HFONT hPieceFont = NULL;
\r
1765 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1766 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1767 static int fontBitmapSquareSize = 0;
\r
1768 static char pieceToFontChar[(int) EmptySquare] =
\r
1769 { 'p', 'n', 'b', 'r', 'q',
\r
1770 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1771 'k', 'o', 'm', 'v', 't', 'w',
\r
1772 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1775 extern BOOL SetCharTable( char *table, const char * map );
\r
1776 /* [HGM] moved to backend.c */
\r
1778 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1781 BYTE r1 = GetRValue( color );
\r
1782 BYTE g1 = GetGValue( color );
\r
1783 BYTE b1 = GetBValue( color );
\r
1789 /* Create a uniform background first */
\r
1790 hbrush = CreateSolidBrush( color );
\r
1791 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1792 FillRect( hdc, &rc, hbrush );
\r
1793 DeleteObject( hbrush );
\r
1796 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1797 int steps = squareSize / 2;
\r
1800 for( i=0; i<steps; i++ ) {
\r
1801 BYTE r = r1 - (r1-r2) * i / steps;
\r
1802 BYTE g = g1 - (g1-g2) * i / steps;
\r
1803 BYTE b = b1 - (b1-b2) * i / steps;
\r
1805 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1806 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1807 FillRect( hdc, &rc, hbrush );
\r
1808 DeleteObject(hbrush);
\r
1811 else if( mode == 2 ) {
\r
1812 /* Diagonal gradient, good more or less for every piece */
\r
1813 POINT triangle[3];
\r
1814 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1815 HBRUSH hbrush_old;
\r
1816 int steps = squareSize;
\r
1819 triangle[0].x = squareSize - steps;
\r
1820 triangle[0].y = squareSize;
\r
1821 triangle[1].x = squareSize;
\r
1822 triangle[1].y = squareSize;
\r
1823 triangle[2].x = squareSize;
\r
1824 triangle[2].y = squareSize - steps;
\r
1826 for( i=0; i<steps; i++ ) {
\r
1827 BYTE r = r1 - (r1-r2) * i / steps;
\r
1828 BYTE g = g1 - (g1-g2) * i / steps;
\r
1829 BYTE b = b1 - (b1-b2) * i / steps;
\r
1831 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1832 hbrush_old = SelectObject( hdc, hbrush );
\r
1833 Polygon( hdc, triangle, 3 );
\r
1834 SelectObject( hdc, hbrush_old );
\r
1835 DeleteObject(hbrush);
\r
1840 SelectObject( hdc, hpen );
\r
1845 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1846 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1847 piece: follow the steps as explained below.
\r
1849 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1853 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1859 int backColor = whitePieceColor;
\r
1860 int foreColor = blackPieceColor;
\r
1862 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1863 backColor = appData.fontBackColorWhite;
\r
1864 foreColor = appData.fontForeColorWhite;
\r
1866 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1867 backColor = appData.fontBackColorBlack;
\r
1868 foreColor = appData.fontForeColorBlack;
\r
1872 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1874 hbm_old = SelectObject( hdc, hbm );
\r
1878 rc.right = squareSize;
\r
1879 rc.bottom = squareSize;
\r
1881 /* Step 1: background is now black */
\r
1882 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1884 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1886 pt.x = (squareSize - sz.cx) / 2;
\r
1887 pt.y = (squareSize - sz.cy) / 2;
\r
1889 SetBkMode( hdc, TRANSPARENT );
\r
1890 SetTextColor( hdc, chroma );
\r
1891 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1892 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1894 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1895 /* Step 3: the area outside the piece is filled with white */
\r
1896 // FloodFill( hdc, 0, 0, chroma );
\r
1897 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1898 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1899 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1900 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1901 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1903 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1904 but if the start point is not inside the piece we're lost!
\r
1905 There should be a better way to do this... if we could create a region or path
\r
1906 from the fill operation we would be fine for example.
\r
1908 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1909 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1911 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1912 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1913 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1915 SelectObject( dc2, bm2 );
\r
1916 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1917 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1918 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1919 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1920 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1923 DeleteObject( bm2 );
\r
1926 SetTextColor( hdc, 0 );
\r
1928 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1929 draw the piece again in black for safety.
\r
1931 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1933 SelectObject( hdc, hbm_old );
\r
1935 if( hPieceMask[index] != NULL ) {
\r
1936 DeleteObject( hPieceMask[index] );
\r
1939 hPieceMask[index] = hbm;
\r
1942 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1944 SelectObject( hdc, hbm );
\r
1947 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1948 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1949 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1951 SelectObject( dc1, hPieceMask[index] );
\r
1952 SelectObject( dc2, bm2 );
\r
1953 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1954 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1957 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1958 the piece background and deletes (makes transparent) the rest.
\r
1959 Thanks to that mask, we are free to paint the background with the greates
\r
1960 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1961 We use this, to make gradients and give the pieces a "roundish" look.
\r
1963 SetPieceBackground( hdc, backColor, 2 );
\r
1964 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1968 DeleteObject( bm2 );
\r
1971 SetTextColor( hdc, foreColor );
\r
1972 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1974 SelectObject( hdc, hbm_old );
\r
1976 if( hPieceFace[index] != NULL ) {
\r
1977 DeleteObject( hPieceFace[index] );
\r
1980 hPieceFace[index] = hbm;
\r
1983 static int TranslatePieceToFontPiece( int piece )
\r
2013 case BlackMarshall:
\r
2017 case BlackNightrider:
\r
2023 case BlackUnicorn:
\r
2027 case BlackGrasshopper:
\r
2039 case BlackCardinal:
\r
2046 case WhiteMarshall:
\r
2050 case WhiteNightrider:
\r
2056 case WhiteUnicorn:
\r
2060 case WhiteGrasshopper:
\r
2072 case WhiteCardinal:
\r
2081 void CreatePiecesFromFont()
\r
2084 HDC hdc_window = NULL;
\r
2090 if( fontBitmapSquareSize < 0 ) {
\r
2091 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
2095 if( !appData.useFont || appData.renderPiecesWithFont == NULL ||
\r
2096 appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
2097 fontBitmapSquareSize = -1;
\r
2101 if( fontBitmapSquareSize != squareSize ) {
\r
2102 hdc_window = GetDC( hwndMain );
\r
2103 hdc = CreateCompatibleDC( hdc_window );
\r
2105 if( hPieceFont != NULL ) {
\r
2106 DeleteObject( hPieceFont );
\r
2109 for( i=0; i<=(int)BlackKing; i++ ) {
\r
2110 hPieceMask[i] = NULL;
\r
2111 hPieceFace[i] = NULL;
\r
2117 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
2118 fontHeight = appData.fontPieceSize;
\r
2121 fontHeight = (fontHeight * squareSize) / 100;
\r
2123 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2125 lf.lfEscapement = 0;
\r
2126 lf.lfOrientation = 0;
\r
2127 lf.lfWeight = FW_NORMAL;
\r
2129 lf.lfUnderline = 0;
\r
2130 lf.lfStrikeOut = 0;
\r
2131 lf.lfCharSet = DEFAULT_CHARSET;
\r
2132 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2133 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2134 lf.lfQuality = PROOF_QUALITY;
\r
2135 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2136 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2137 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2139 hPieceFont = CreateFontIndirect( &lf );
\r
2141 if( hPieceFont == NULL ) {
\r
2142 fontBitmapSquareSize = -2;
\r
2145 /* Setup font-to-piece character table */
\r
2146 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2147 /* No (or wrong) global settings, try to detect the font */
\r
2148 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2150 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2152 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2153 /* DiagramTT* family */
\r
2154 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2156 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2157 /* Fairy symbols */
\r
2158 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2160 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2161 /* Good Companion (Some characters get warped as literal :-( */
\r
2162 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2163 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2164 SetCharTable(pieceToFontChar, s);
\r
2167 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2168 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2172 /* Create bitmaps */
\r
2173 hfont_old = SelectObject( hdc, hPieceFont );
\r
2174 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2175 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2176 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2178 SelectObject( hdc, hfont_old );
\r
2180 fontBitmapSquareSize = squareSize;
\r
2184 if( hdc != NULL ) {
\r
2188 if( hdc_window != NULL ) {
\r
2189 ReleaseDC( hwndMain, hdc_window );
\r
2194 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2196 char name[128], buf[MSG_SIZ];
\r
2198 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2199 if(appData.pieceDirectory[0]) {
\r
2201 snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);
\r
2202 res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
2203 if(res) return res;
\r
2205 if (gameInfo.event &&
\r
2206 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2207 strcmp(name, "k80s") == 0) {
\r
2208 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2210 return LoadBitmap(hinst, name);
\r
2214 /* Insert a color into the program's logical palette
\r
2215 structure. This code assumes the given color is
\r
2216 the result of the RGB or PALETTERGB macro, and it
\r
2217 knows how those macros work (which is documented).
\r
2220 InsertInPalette(COLORREF color)
\r
2222 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2224 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2225 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2226 pLogPal->palNumEntries--;
\r
2230 pe->peFlags = (char) 0;
\r
2231 pe->peRed = (char) (0xFF & color);
\r
2232 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2233 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2239 InitDrawingColors()
\r
2242 if (pLogPal == NULL) {
\r
2243 /* Allocate enough memory for a logical palette with
\r
2244 * PALETTESIZE entries and set the size and version fields
\r
2245 * of the logical palette structure.
\r
2247 pLogPal = (NPLOGPALETTE)
\r
2248 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2249 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2250 pLogPal->palVersion = 0x300;
\r
2252 pLogPal->palNumEntries = 0;
\r
2254 InsertInPalette(lightSquareColor);
\r
2255 InsertInPalette(darkSquareColor);
\r
2256 InsertInPalette(whitePieceColor);
\r
2257 InsertInPalette(blackPieceColor);
\r
2258 InsertInPalette(highlightSquareColor);
\r
2259 InsertInPalette(premoveHighlightColor);
\r
2261 /* create a logical color palette according the information
\r
2262 * in the LOGPALETTE structure.
\r
2264 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2266 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2267 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2268 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2269 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2270 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2271 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2272 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2273 for(i=0; i<8;i++) markerBrush[i] = CreateSolidBrush(markerColor[i]); // [HGM] markers
\r
2275 /* [AS] Force rendering of the font-based pieces */
\r
2276 if( fontBitmapSquareSize > 0 ) {
\r
2277 fontBitmapSquareSize = 0;
\r
2283 BoardWidth(int boardSize, int n)
\r
2284 { /* [HGM] argument n added to allow different width and height */
\r
2285 int lineGap = sizeInfo[boardSize].lineGap;
\r
2287 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2288 lineGap = appData.overrideLineGap;
\r
2291 return (n + 1) * lineGap +
\r
2292 n * sizeInfo[boardSize].squareSize;
\r
2295 /* Respond to board resize by dragging edge */
\r
2297 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2299 BoardSize newSize = NUM_SIZES - 1;
\r
2300 static int recurse = 0;
\r
2301 if (IsIconic(hwndMain)) return;
\r
2302 if (recurse > 0) return;
\r
2304 while (newSize > 0) {
\r
2305 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2306 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2307 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2310 boardSize = newSize;
\r
2311 InitDrawingSizes(boardSize, flags);
\r
2316 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2319 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2321 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2322 ChessSquare piece;
\r
2323 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2325 SIZE clockSize, messageSize;
\r
2327 char buf[MSG_SIZ];
\r
2329 HMENU hmenu = GetMenu(hwndMain);
\r
2330 RECT crect, wrect, oldRect;
\r
2332 LOGBRUSH logbrush;
\r
2333 VariantClass v = gameInfo.variant;
\r
2335 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2336 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2338 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2339 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2340 if(boardSize == -1) return; // no size defined yet; abort (to allow early call of InitPosition)
\r
2341 oldBoardSize = boardSize;
\r
2343 if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)
\r
2344 { // correct board size to one where built-in pieces exist
\r
2345 if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)
\r
2346 && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range
\r
2348 || (v == VariantShogi && boardSize != SizeModerate) // Japanese-style Shogi
\r
2349 || v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan
\r
2350 || v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy || v == VariantLion ) {
\r
2351 if(boardSize < SizeMediocre) boardSize = SizePetite; else
\r
2352 if(boardSize > SizeModerate) boardSize = SizeBulky; else
\r
2353 boardSize = SizeMiddling;
\r
2356 if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite
\r
2358 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2359 oldRect.top = wpMain.y;
\r
2360 oldRect.right = wpMain.x + wpMain.width;
\r
2361 oldRect.bottom = wpMain.y + wpMain.height;
\r
2363 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2364 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2365 squareSize = sizeInfo[boardSize].squareSize;
\r
2366 lineGap = sizeInfo[boardSize].lineGap;
\r
2367 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2368 border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;
\r
2370 // [HGM] decide on tininess based on total board width rather than square size
\r
2371 tinyLayout = squareSize * (BOARD_WIDTH);
\r
2372 tinyLayout = tinyLayout < 35*8 ? 2 : tinyLayout < 43*8 ? 1 : 0;
\r
2374 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2375 lineGap = appData.overrideLineGap;
\r
2378 if (tinyLayout != oldTinyLayout) {
\r
2379 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2380 if (tinyLayout == 2) {
\r
2381 style &= ~WS_SYSMENU;
\r
2382 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2383 "&Minimize\tCtrl+F4");
\r
2385 style |= WS_SYSMENU;
\r
2386 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2388 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2390 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2391 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2392 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2394 DrawMenuBar(hwndMain);
\r
2397 boardWidth = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;
\r
2398 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;
\r
2400 /* Get text area sizes */
\r
2401 hdc = GetDC(hwndMain);
\r
2402 if (appData.clockMode) {
\r
2403 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2405 snprintf(buf, MSG_SIZ, _("White"));
\r
2407 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2408 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2409 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2410 str = _("We only care about the height here");
\r
2411 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2412 SelectObject(hdc, oldFont);
\r
2413 ReleaseDC(hwndMain, hdc);
\r
2415 /* Compute where everything goes */
\r
2416 if((first.programLogo || second.programLogo) && tinyLayout != 2) {
\r
2417 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2418 logoHeight = 2*clockSize.cy;
\r
2419 leftLogoRect.left = OUTER_MARGIN;
\r
2420 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2421 leftLogoRect.top = OUTER_MARGIN;
\r
2422 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2424 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2425 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2426 rightLogoRect.top = OUTER_MARGIN;
\r
2427 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2430 whiteRect.left = leftLogoRect.right;
\r
2431 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2432 whiteRect.top = OUTER_MARGIN;
\r
2433 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2435 blackRect.right = rightLogoRect.left;
\r
2436 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2437 blackRect.top = whiteRect.top;
\r
2438 blackRect.bottom = whiteRect.bottom;
\r
2440 whiteRect.left = OUTER_MARGIN;
\r
2441 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2442 whiteRect.top = OUTER_MARGIN;
\r
2443 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2445 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2446 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2447 blackRect.top = whiteRect.top;
\r
2448 blackRect.bottom = whiteRect.bottom;
\r
2450 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2453 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2454 if (appData.showButtonBar) {
\r
2455 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2456 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2458 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2460 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2461 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2463 boardRect.left = OUTER_MARGIN;
\r
2464 boardRect.right = boardRect.left + boardWidth;
\r
2465 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2466 boardRect.bottom = boardRect.top + boardHeight;
\r
2468 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2469 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2470 oldTinyLayout = tinyLayout;
\r
2471 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2472 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2473 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2474 winW *= 1 + twoBoards;
\r
2475 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2476 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2477 wpMain.height = winH; // without disturbing window attachments
\r
2478 GetWindowRect(hwndMain, &wrect);
\r
2479 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2480 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2482 // [HGM] placement: let attached windows follow size change.
\r
2483 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2484 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2485 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2486 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2487 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2489 /* compensate if menu bar wrapped */
\r
2490 GetClientRect(hwndMain, &crect);
\r
2491 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2492 wpMain.height += offby;
\r
2494 case WMSZ_TOPLEFT:
\r
2495 SetWindowPos(hwndMain, NULL,
\r
2496 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2497 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2500 case WMSZ_TOPRIGHT:
\r
2502 SetWindowPos(hwndMain, NULL,
\r
2503 wrect.left, wrect.bottom - wpMain.height,
\r
2504 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2507 case WMSZ_BOTTOMLEFT:
\r
2509 SetWindowPos(hwndMain, NULL,
\r
2510 wrect.right - wpMain.width, wrect.top,
\r
2511 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2514 case WMSZ_BOTTOMRIGHT:
\r
2518 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2519 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2524 for (i = 0; i < N_BUTTONS; i++) {
\r
2525 if (buttonDesc[i].hwnd != NULL) {
\r
2526 DestroyWindow(buttonDesc[i].hwnd);
\r
2527 buttonDesc[i].hwnd = NULL;
\r
2529 if (appData.showButtonBar) {
\r
2530 buttonDesc[i].hwnd =
\r
2531 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2532 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2533 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2534 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2535 (HMENU) buttonDesc[i].id,
\r
2536 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2537 if (tinyLayout == 2) {
\r
2538 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2539 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2540 MAKELPARAM(FALSE, 0));
\r
2542 if (buttonDesc[i].id == IDM_Pause)
\r
2543 hwndPause = buttonDesc[i].hwnd;
\r
2544 buttonDesc[i].wndproc = (WNDPROC)
\r
2545 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2548 if (gridPen != NULL) DeleteObject(gridPen);
\r
2549 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2550 if (premovePen != NULL) DeleteObject(premovePen);
\r
2551 if (lineGap != 0) {
\r
2552 logbrush.lbStyle = BS_SOLID;
\r
2553 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2555 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2556 lineGap, &logbrush, 0, NULL);
\r
2557 logbrush.lbColor = highlightSquareColor;
\r
2559 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2560 lineGap, &logbrush, 0, NULL);
\r
2562 logbrush.lbColor = premoveHighlightColor;
\r
2564 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2565 lineGap, &logbrush, 0, NULL);
\r
2567 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2568 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2569 gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;
\r
2570 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2571 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;
\r
2572 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2573 BOARD_WIDTH * (squareSize + lineGap) + border;
\r
2574 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2576 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2577 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;
\r
2578 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2579 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2580 lineGap / 2 + (i * (squareSize + lineGap)) + border;
\r
2581 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2582 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;
\r
2583 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2587 /* [HGM] Licensing requirement */
\r
2589 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2592 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2594 GothicPopUp( "", VariantNormal);
\r
2597 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2599 /* Load piece bitmaps for this board size */
\r
2600 for (i=0; i<=2; i++) {
\r
2601 for (piece = WhitePawn;
\r
2602 (int) piece < (int) BlackPawn;
\r
2603 piece = (ChessSquare) ((int) piece + 1)) {
\r
2604 if (pieceBitmap[i][piece] != NULL)
\r
2605 DeleteObject(pieceBitmap[i][piece]);
\r
2606 pieceBitmap[i][piece] = NULL;
\r
2610 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2612 // Orthodox Chess pieces
\r
2613 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2614 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2615 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2616 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2617 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2618 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2619 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2620 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2621 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2622 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2623 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2624 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2625 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2626 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2627 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2628 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2629 // in Shogi, Hijack the unused Queen for Lance
\r
2630 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2631 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2632 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2634 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2635 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2636 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2639 if(squareSize <= 72 && squareSize >= 33) {
\r
2640 /* A & C are available in most sizes now */
\r
2641 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2642 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2643 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2644 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2645 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2646 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2647 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2648 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2649 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2650 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2651 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2652 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2653 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2654 } else { // Smirf-like
\r
2655 if(gameInfo.variant == VariantSChess) {
\r
2656 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2657 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2658 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2660 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2661 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2662 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2665 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2666 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2667 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2668 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2669 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2670 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2671 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2672 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2673 } else { // WinBoard standard
\r
2674 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2675 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2676 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2681 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2682 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2683 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2684 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2685 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2686 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2687 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2688 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2689 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2690 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2691 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2692 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2693 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2694 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2695 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2696 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2697 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2698 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2699 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2700 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2701 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2702 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2703 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2704 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2705 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2706 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2707 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2708 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2709 pieceBitmap[0][WhiteAmazon] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2710 pieceBitmap[1][WhiteAmazon] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2711 pieceBitmap[2][WhiteAmazon] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2712 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2713 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2714 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2715 pieceBitmap[0][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "s");
\r
2716 pieceBitmap[1][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "o");
\r
2717 pieceBitmap[2][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "w");
\r
2718 pieceBitmap[0][WhiteCub] = DoLoadBitmap(hInst, "ln", squareSize, "s");
\r
2719 pieceBitmap[1][WhiteCub] = DoLoadBitmap(hInst, "ln", squareSize, "o");
\r
2720 pieceBitmap[2][WhiteCub] = DoLoadBitmap(hInst, "ln", squareSize, "w");
\r
2721 pieceBitmap[0][WhiteWolf] = DoLoadBitmap(hInst, "wolf", squareSize, "s");
\r
2722 pieceBitmap[1][WhiteWolf] = DoLoadBitmap(hInst, "wolf", squareSize, "o");
\r
2723 pieceBitmap[2][WhiteWolf] = DoLoadBitmap(hInst, "wolf", squareSize, "w");
\r
2724 pieceBitmap[0][WhiteCamel] = DoLoadBitmap(hInst, "camel", squareSize, "s");
\r
2725 pieceBitmap[1][WhiteCamel] = DoLoadBitmap(hInst, "camel", squareSize, "o");
\r
2726 pieceBitmap[2][WhiteCamel] = DoLoadBitmap(hInst, "camel", squareSize, "w");
\r
2727 pieceBitmap[0][WhiteZebra] = DoLoadBitmap(hInst, "zebra", squareSize, "s");
\r
2728 pieceBitmap[1][WhiteZebra] = DoLoadBitmap(hInst, "zebra", squareSize, "o");
\r
2729 pieceBitmap[2][WhiteZebra] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2730 pieceBitmap[0][WhiteTower] = DoLoadBitmap(hInst, "tower", squareSize, "s");
\r
2731 pieceBitmap[1][WhiteTower] = DoLoadBitmap(hInst, "tower", squareSize, "o");
\r
2732 pieceBitmap[2][WhiteTower] = DoLoadBitmap(hInst, "tower", squareSize, "w");
\r
2733 pieceBitmap[0][WhiteSword] = DoLoadBitmap(hInst, "sword", squareSize, "s");
\r
2734 pieceBitmap[1][WhiteSword] = DoLoadBitmap(hInst, "sword", squareSize, "o");
\r
2735 pieceBitmap[2][WhiteSword] = DoLoadBitmap(hInst, "sword", squareSize, "w");
\r
2736 pieceBitmap[0][WhiteGnu] = DoLoadBitmap(hInst, "gnu", squareSize, "s");
\r
2737 pieceBitmap[1][WhiteGnu] = DoLoadBitmap(hInst, "gnu", squareSize, "o");
\r
2738 pieceBitmap[2][WhiteGnu] = DoLoadBitmap(hInst, "gnu", squareSize, "w");
\r
2740 if(gameInfo.variant == VariantShogi && BOARD_HEIGHT != 7) { /* promoted Gold representations (but not in Tori!)*/
\r
2741 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2742 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2743 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2744 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2745 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2746 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2747 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2748 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2749 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2750 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2751 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2752 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2754 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2755 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2756 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2757 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2758 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2759 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2760 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2761 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2762 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2763 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2764 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2765 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2768 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2769 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2770 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2771 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2772 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2773 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2774 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2775 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2776 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2777 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2778 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2779 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2780 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2781 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2782 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2786 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2787 /* special Shogi support in this size */
\r
2788 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2789 for (piece = WhitePawn;
\r
2790 (int) piece < (int) BlackPawn;
\r
2791 piece = (ChessSquare) ((int) piece + 1)) {
\r
2792 if (pieceBitmap[i][piece] != NULL)
\r
2793 DeleteObject(pieceBitmap[i][piece]);
\r
2796 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2797 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2798 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2799 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2800 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2801 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2802 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2803 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2804 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2805 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2806 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2807 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2808 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2809 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2810 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2811 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2812 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2813 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2814 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2815 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2816 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2817 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2818 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2819 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2820 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2821 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2822 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2823 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2824 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2825 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2826 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2827 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2828 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2829 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2830 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2831 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2832 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2833 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2834 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2835 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2836 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2837 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2841 if(appData.pieceDirectory[0]) for(i=WhitePawn; i<BlackPawn; i++) { // try for all missing pieces with new naming convention
\r
2842 char buf[MSG_SIZ];
\r
2843 if(pieceBitmap[0][i]) continue;
\r
2844 snprintf(buf, MSG_SIZ, "piece%d_", i);
\r
2845 pieceBitmap[0][i] = DoLoadBitmap(hInst, buf, squareSize, "s");
\r
2846 pieceBitmap[1][i] = DoLoadBitmap(hInst, buf, squareSize, "o");
\r
2847 pieceBitmap[2][i] = DoLoadBitmap(hInst, buf, squareSize, "w");
\r
2852 PieceBitmap(ChessSquare p, int kind)
\r
2854 if ((int) p >= (int) BlackPawn)
\r
2855 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2857 return pieceBitmap[kind][(int) p];
\r
2860 /***************************************************************/
\r
2862 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2863 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2865 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2866 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2870 SquareToPos(int row, int column, int * x, int * y)
\r
2873 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
2874 *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;
\r
2876 *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;
\r
2877 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
2882 DrawCoordsOnDC(HDC hdc)
\r
2884 static char files[] = "0123456789012345678901221098765432109876543210";
\r
2885 static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";
\r
2886 char str[2] = { NULLCHAR, NULLCHAR };
\r
2887 int oldMode, oldAlign, x, y, start, i;
\r
2891 if (!appData.showCoords)
\r
2894 start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;
\r
2896 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2897 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2898 oldAlign = GetTextAlign(hdc);
\r
2899 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2901 y = boardRect.top + lineGap;
\r
2902 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2905 SetTextAlign(hdc, TA_RIGHT|TA_TOP);
\r
2906 x += border - lineGap - 4; y += squareSize - 6;
\r
2908 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2909 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2910 str[0] = files[start + i];
\r
2911 ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);
\r
2912 y += squareSize + lineGap;
\r
2915 start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;
\r
2918 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2919 x += -border + 4; y += border - squareSize + 6;
\r
2921 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2922 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2923 str[0] = ranks[start + i];
\r
2924 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2925 x += squareSize + lineGap;
\r
2928 SelectObject(hdc, oldBrush);
\r
2929 SetBkMode(hdc, oldMode);
\r
2930 SetTextAlign(hdc, oldAlign);
\r
2931 SelectObject(hdc, oldFont);
\r
2935 DrawGridOnDC(HDC hdc)
\r
2939 if (lineGap != 0) {
\r
2940 oldPen = SelectObject(hdc, gridPen);
\r
2941 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2942 SelectObject(hdc, oldPen);
\r
2946 #define HIGHLIGHT_PEN 0
\r
2947 #define PREMOVE_PEN 1
\r
2950 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2953 HPEN oldPen, hPen;
\r
2954 if (lineGap == 0) return;
\r
2956 x1 = boardRect.left +
\r
2957 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;
\r
2958 y1 = boardRect.top +
\r
2959 lineGap/2 + y * (squareSize + lineGap) + border;
\r
2961 x1 = boardRect.left +
\r
2962 lineGap/2 + x * (squareSize + lineGap) + border;
\r
2963 y1 = boardRect.top +
\r
2964 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;
\r
2966 hPen = pen ? premovePen : highlightPen;
\r
2967 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2968 MoveToEx(hdc, x1, y1, NULL);
\r
2969 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2970 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2971 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2972 LineTo(hdc, x1, y1);
\r
2973 SelectObject(hdc, oldPen);
\r
2977 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2980 for (i=0; i<2; i++) {
\r
2981 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2982 DrawHighlightOnDC(hdc, TRUE,
\r
2983 h->sq[i].x, h->sq[i].y,
\r
2988 /* Note: sqcolor is used only in monoMode */
\r
2989 /* Note that this code is largely duplicated in woptions.c,
\r
2990 function DrawSampleSquare, so that needs to be updated too */
\r
2992 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2994 HBITMAP oldBitmap;
\r
2998 if (appData.blindfold) return;
\r
3000 /* [AS] Use font-based pieces if needed */
\r
3001 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
3002 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
3003 CreatePiecesFromFont();
\r
3005 if( fontBitmapSquareSize == squareSize ) {
\r
3006 int index = TranslatePieceToFontPiece(piece);
\r
3008 SelectObject( tmphdc, hPieceMask[ index ] );
\r
3010 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
3011 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
3015 squareSize, squareSize,
\r
3020 SelectObject( tmphdc, hPieceFace[ index ] );
\r
3022 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
3023 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
3027 squareSize, squareSize,
\r
3036 if (appData.monoMode) {
\r
3037 SelectObject(tmphdc, PieceBitmap(piece,
\r
3038 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
3039 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
3040 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
3042 HBRUSH xBrush = whitePieceBrush;
\r
3043 tmpSize = squareSize;
\r
3044 if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);
\r
3046 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
3047 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
3048 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
3049 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
3050 x += (squareSize - minorSize)>>1;
\r
3051 y += squareSize - minorSize - 2;
\r
3052 tmpSize = minorSize;
\r
3054 if (color || appData.allWhite ) {
\r
3055 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
3057 oldBrush = SelectObject(hdc, xBrush);
\r
3058 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
3059 if(appData.upsideDown && color==flipView)
\r
3060 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
3062 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
3063 /* Use black for outline of white pieces */
\r
3064 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
3065 if(appData.upsideDown && color==flipView)
\r
3066 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
3068 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
3069 } else if(appData.pieceDirectory[0]) {
\r
3070 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
3071 oldBrush = SelectObject(hdc, xBrush);
\r
3072 if(appData.upsideDown && color==flipView)
\r
3073 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
3075 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
3076 SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
3077 if(appData.upsideDown && color==flipView)
\r
3078 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
3080 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
3082 /* Use square color for details of black pieces */
\r
3083 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
3084 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
3085 if(appData.upsideDown && !flipView)
\r
3086 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
3088 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
3090 SelectObject(hdc, oldBrush);
\r
3091 SelectObject(tmphdc, oldBitmap);
\r
3095 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
3096 int GetBackTextureMode( int algo )
\r
3098 int result = BACK_TEXTURE_MODE_DISABLED;
\r
3102 case BACK_TEXTURE_MODE_PLAIN:
\r
3103 result = 1; /* Always use identity map */
\r
3105 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
3106 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
3114 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
3115 to handle redraws cleanly (as random numbers would always be different).
\r
3117 VOID RebuildTextureSquareInfo()
\r
3127 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
3129 if( liteBackTexture != NULL ) {
\r
3130 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3131 lite_w = bi.bmWidth;
\r
3132 lite_h = bi.bmHeight;
\r
3136 if( darkBackTexture != NULL ) {
\r
3137 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3138 dark_w = bi.bmWidth;
\r
3139 dark_h = bi.bmHeight;
\r
3143 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
3144 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
3145 if( (col + row) & 1 ) {
\r
3147 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
3148 if( lite_w >= squareSize*BOARD_WIDTH )
\r
3149 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
3151 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
3152 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
3153 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
3155 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
3156 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
3161 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
3162 if( dark_w >= squareSize*BOARD_WIDTH )
\r
3163 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
3165 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
3166 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
3167 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
3169 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
3170 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
3177 /* [AS] Arrow highlighting support */
\r
3179 static double A_WIDTH = 5; /* Width of arrow body */
\r
3181 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
3182 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
3184 static double Sqr( double x )
\r
3189 static int Round( double x )
\r
3191 return (int) (x + 0.5);
\r
3194 /* Draw an arrow between two points using current settings */
\r
3195 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
3198 double dx, dy, j, k, x, y;
\r
3200 if( d_x == s_x ) {
\r
3201 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3203 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
3206 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
3207 arrow[1].y = d_y - h;
\r
3209 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3210 arrow[2].y = d_y - h;
\r
3215 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3216 arrow[5].y = d_y - h;
\r
3218 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3219 arrow[4].y = d_y - h;
\r
3221 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3224 else if( d_y == s_y ) {
\r
3225 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3228 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3230 arrow[1].x = d_x - w;
\r
3231 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3233 arrow[2].x = d_x - w;
\r
3234 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3239 arrow[5].x = d_x - w;
\r
3240 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3242 arrow[4].x = d_x - w;
\r
3243 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3246 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3249 /* [AS] Needed a lot of paper for this! :-) */
\r
3250 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3251 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3253 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3255 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3260 arrow[0].x = Round(x - j);
\r
3261 arrow[0].y = Round(y + j*dx);
\r
3263 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3264 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3267 x = (double) d_x - k;
\r
3268 y = (double) d_y - k*dy;
\r
3271 x = (double) d_x + k;
\r
3272 y = (double) d_y + k*dy;
\r
3275 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3277 arrow[6].x = Round(x - j);
\r
3278 arrow[6].y = Round(y + j*dx);
\r
3280 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3281 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3283 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3284 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3289 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3290 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3293 Polygon( hdc, arrow, 7 );
\r
3296 /* [AS] Draw an arrow between two squares */
\r
3297 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3299 int s_x, s_y, d_x, d_y;
\r
3306 if( s_col == d_col && s_row == d_row ) {
\r
3310 /* Get source and destination points */
\r
3311 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3312 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3315 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3317 else if( d_y < s_y ) {
\r
3318 d_y += squareSize / 2 + squareSize / 4;
\r
3321 d_y += squareSize / 2;
\r
3325 d_x += squareSize / 2 - squareSize / 4;
\r
3327 else if( d_x < s_x ) {
\r
3328 d_x += squareSize / 2 + squareSize / 4;
\r
3331 d_x += squareSize / 2;
\r
3334 s_x += squareSize / 2;
\r
3335 s_y += squareSize / 2;
\r
3337 /* Adjust width */
\r
3338 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3341 stLB.lbStyle = BS_SOLID;
\r
3342 stLB.lbColor = appData.highlightArrowColor;
\r
3345 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3346 holdpen = SelectObject( hdc, hpen );
\r
3347 hbrush = CreateBrushIndirect( &stLB );
\r
3348 holdbrush = SelectObject( hdc, hbrush );
\r
3350 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3352 SelectObject( hdc, holdpen );
\r
3353 SelectObject( hdc, holdbrush );
\r
3354 DeleteObject( hpen );
\r
3355 DeleteObject( hbrush );
\r
3358 BOOL HasHighlightInfo()
\r
3360 BOOL result = FALSE;
\r
3362 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3363 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3374 BOOL IsDrawArrowEnabled()
\r
3376 BOOL result = FALSE;
\r
3378 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3385 VOID DrawArrowHighlight( HDC hdc )
\r
3387 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3388 DrawArrowBetweenSquares( hdc,
\r
3389 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3390 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3394 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3396 HRGN result = NULL;
\r
3398 if( HasHighlightInfo() ) {
\r
3399 int x1, y1, x2, y2;
\r
3400 int sx, sy, dx, dy;
\r
3402 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3403 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3405 sx = MIN( x1, x2 );
\r
3406 sy = MIN( y1, y2 );
\r
3407 dx = MAX( x1, x2 ) + squareSize;
\r
3408 dy = MAX( y1, y2 ) + squareSize;
\r
3410 result = CreateRectRgn( sx, sy, dx, dy );
\r
3417 Warning: this function modifies the behavior of several other functions.
\r
3419 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3420 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3421 repaint is scattered all over the place, which is not good for features such as
\r
3422 "arrow highlighting" that require a full repaint of the board.
\r
3424 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3425 user interaction, when speed is not so important) but especially to avoid errors
\r
3426 in the displayed graphics.
\r
3428 In such patched places, I always try refer to this function so there is a single
\r
3429 place to maintain knowledge.
\r
3431 To restore the original behavior, just return FALSE unconditionally.
\r
3433 BOOL IsFullRepaintPreferrable()
\r
3435 BOOL result = FALSE;
\r
3437 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3438 /* Arrow may appear on the board */
\r
3446 This function is called by DrawPosition to know whether a full repaint must
\r
3449 Only DrawPosition may directly call this function, which makes use of
\r
3450 some state information. Other function should call DrawPosition specifying
\r
3451 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3453 BOOL DrawPositionNeedsFullRepaint()
\r
3455 BOOL result = FALSE;
\r
3458 Probably a slightly better policy would be to trigger a full repaint
\r
3459 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3460 but animation is fast enough that it's difficult to notice.
\r
3462 if( animInfo.piece == EmptySquare ) {
\r
3463 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3471 static HBITMAP borderBitmap;
\r
3474 DrawBackgroundOnDC(HDC hdc)
\r
3480 static char oldBorder[MSG_SIZ];
\r
3481 int w = 600, h = 600, mode;
\r
3483 if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid
\r
3484 strncpy(oldBorder, appData.border, MSG_SIZ-1);
\r
3485 borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
3487 if(borderBitmap == NULL) { // loading failed, use white
\r
3488 FillRect( hdc, &boardRect, whitePieceBrush );
\r
3491 tmphdc = CreateCompatibleDC(hdc);
\r
3492 hbm = SelectObject(tmphdc, borderBitmap);
\r
3493 if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {
\r
3497 mode = SetStretchBltMode(hdc, COLORONCOLOR);
\r
3498 StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left,
\r
3499 boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3500 SetStretchBltMode(hdc, mode);
\r
3501 SelectObject(tmphdc, hbm);
\r
3506 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3508 int row, column, x, y, square_color, piece_color;
\r
3509 ChessSquare piece;
\r
3511 HDC texture_hdc = NULL;
\r
3513 /* [AS] Initialize background textures if needed */
\r
3514 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3515 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3516 if( backTextureSquareSize != squareSize
\r
3517 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3518 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3519 backTextureSquareSize = squareSize;
\r
3520 RebuildTextureSquareInfo();
\r
3523 texture_hdc = CreateCompatibleDC( hdc );
\r
3526 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3527 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3529 SquareToPos(row, column, &x, &y);
\r
3531 piece = board[row][column];
\r
3533 square_color = ((column + row) % 2) == 1;
\r
3534 if( gameInfo.variant == VariantXiangqi ) {
\r
3535 square_color = !InPalace(row, column);
\r
3536 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3537 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3539 piece_color = (int) piece < (int) BlackPawn;
\r
3542 /* [HGM] holdings file: light square or black */
\r
3543 if(column == BOARD_LEFT-2) {
\r
3544 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3547 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3551 if(column == BOARD_RGHT + 1 ) {
\r
3552 if( row < gameInfo.holdingsSize )
\r
3555 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3559 if(column == BOARD_LEFT-1 ) /* left align */
\r
3560 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3561 else if( column == BOARD_RGHT) /* right align */
\r
3562 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3563 else if( piece == DarkSquare) DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3565 if (appData.monoMode) {
\r
3566 if (piece == EmptySquare) {
\r
3567 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3568 square_color ? WHITENESS : BLACKNESS);
\r
3570 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3573 else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {
\r
3574 /* [AS] Draw the square using a texture bitmap */
\r
3575 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3576 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3577 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3580 squareSize, squareSize,
\r
3583 backTextureSquareInfo[r][c].mode,
\r
3584 backTextureSquareInfo[r][c].x,
\r
3585 backTextureSquareInfo[r][c].y );
\r
3587 SelectObject( texture_hdc, hbm );
\r
3589 if (piece != EmptySquare) {
\r
3590 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3594 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3596 oldBrush = SelectObject(hdc, brush );
\r
3597 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3598 SelectObject(hdc, oldBrush);
\r
3599 if (piece != EmptySquare)
\r
3600 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3605 if( texture_hdc != NULL ) {
\r
3606 DeleteDC( texture_hdc );
\r
3610 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3611 void fputDW(FILE *f, int x)
\r
3613 fputc(x & 255, f);
\r
3614 fputc(x>>8 & 255, f);
\r
3615 fputc(x>>16 & 255, f);
\r
3616 fputc(x>>24 & 255, f);
\r
3619 #define MAX_CLIPS 200 /* more than enough */
\r
3622 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3624 // HBITMAP bufferBitmap;
\r
3629 int w = 100, h = 50;
\r
3631 if(logo == NULL) {
\r
3632 if(!logoHeight) return;
\r
3633 FillRect( hdc, &logoRect, whitePieceBrush );
\r
3635 // GetClientRect(hwndMain, &Rect);
\r
3636 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3637 // Rect.bottom-Rect.top+1);
\r
3638 tmphdc = CreateCompatibleDC(hdc);
\r
3639 hbm = SelectObject(tmphdc, logo);
\r
3640 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3644 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3645 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3646 SelectObject(tmphdc, hbm);
\r
3654 HDC hdc = GetDC(hwndMain);
\r
3655 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3656 if(appData.autoLogo) {
\r
3658 switch(gameMode) { // pick logos based on game mode
\r
3659 case IcsObserving:
\r
3660 whiteLogo = second.programLogo; // ICS logo
\r
3661 blackLogo = second.programLogo;
\r
3664 case IcsPlayingWhite:
\r
3665 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3666 blackLogo = second.programLogo; // ICS logo
\r
3668 case IcsPlayingBlack:
\r
3669 whiteLogo = second.programLogo; // ICS logo
\r
3670 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3672 case TwoMachinesPlay:
\r
3673 if(first.twoMachinesColor[0] == 'b') {
\r
3674 whiteLogo = second.programLogo;
\r
3675 blackLogo = first.programLogo;
\r
3678 case MachinePlaysWhite:
\r
3679 blackLogo = userLogo;
\r
3681 case MachinePlaysBlack:
\r
3682 whiteLogo = userLogo;
\r
3683 blackLogo = first.programLogo;
\r
3686 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3687 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3688 ReleaseDC(hwndMain, hdc);
\r
3693 UpdateLogos(int display)
\r
3694 { // called after loading new engine(s), in tourney or from menu
\r
3695 LoadLogo(&first, 0, FALSE);
\r
3696 LoadLogo(&second, 1, appData.icsActive);
\r
3697 InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos
\r
3698 if(display) DisplayLogos();
\r
3701 static HDC hdcSeek;
\r
3703 // [HGM] seekgraph
\r
3704 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3707 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3708 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3709 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3710 SelectObject( hdcSeek, hp );
\r
3713 // front-end wrapper for drawing functions to do rectangles
\r
3714 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3719 if (hdcSeek == NULL) {
\r
3720 hdcSeek = GetDC(hwndMain);
\r
3721 if (!appData.monoMode) {
\r
3722 SelectPalette(hdcSeek, hPal, FALSE);
\r
3723 RealizePalette(hdcSeek);
\r
3726 hp = SelectObject( hdcSeek, gridPen );
\r
3727 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3728 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3729 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3730 SelectObject( hdcSeek, hp );
\r
3733 // front-end wrapper for putting text in graph
\r
3734 void DrawSeekText(char *buf, int x, int y)
\r
3737 SetBkMode( hdcSeek, TRANSPARENT );
\r
3738 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3739 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3742 void DrawSeekDot(int x, int y, int color)
\r
3744 int square = color & 0x80;
\r
3745 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3746 color == 0 ? markerBrush[1] : color == 1 ? darkSquareBrush : explodeBrush);
\r
3749 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3750 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3752 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3753 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3754 SelectObject(hdcSeek, oldBrush);
\r
3757 void DrawSeekOpen()
\r
3761 void DrawSeekClose()
\r
3770 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3772 static Board lastReq[2], lastDrawn[2];
\r
3773 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3774 static int lastDrawnFlipView = 0;
\r
3775 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3776 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3779 HBITMAP bufferBitmap;
\r
3780 HBITMAP oldBitmap;
\r
3782 HRGN clips[MAX_CLIPS];
\r
3783 ChessSquare dragged_piece = EmptySquare;
\r
3784 int nr = twoBoards*partnerUp;
\r
3786 /* I'm undecided on this - this function figures out whether a full
\r
3787 * repaint is necessary on its own, so there's no real reason to have the
\r
3788 * caller tell it that. I think this can safely be set to FALSE - but
\r
3789 * if we trust the callers not to request full repaints unnessesarily, then
\r
3790 * we could skip some clipping work. In other words, only request a full
\r
3791 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3792 * gamestart and similar) --Hawk
\r
3794 Boolean fullrepaint = repaint;
\r
3796 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3798 if( DrawPositionNeedsFullRepaint() ) {
\r
3799 fullrepaint = TRUE;
\r
3802 if (board == NULL) {
\r
3803 if (!lastReqValid[nr]) {
\r
3806 board = lastReq[nr];
\r
3808 CopyBoard(lastReq[nr], board);
\r
3809 lastReqValid[nr] = 1;
\r
3812 if (doingSizing) {
\r
3816 if (IsIconic(hwndMain)) {
\r
3820 if (hdc == NULL) {
\r
3821 hdc = GetDC(hwndMain);
\r
3822 if (!appData.monoMode) {
\r
3823 SelectPalette(hdc, hPal, FALSE);
\r
3824 RealizePalette(hdc);
\r
3828 releaseDC = FALSE;
\r
3831 /* Create some work-DCs */
\r
3832 hdcmem = CreateCompatibleDC(hdc);
\r
3833 tmphdc = CreateCompatibleDC(hdc);
\r
3835 /* If dragging is in progress, we temporarely remove the piece */
\r
3836 /* [HGM] or temporarily decrease count if stacked */
\r
3837 /* !! Moved to before board compare !! */
\r
3838 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3839 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3840 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3841 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3842 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3844 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3845 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3846 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3848 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3851 /* Figure out which squares need updating by comparing the
\r
3852 * newest board with the last drawn board and checking if
\r
3853 * flipping has changed.
\r
3855 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3856 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3857 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3858 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3859 SquareToPos(row, column, &x, &y);
\r
3860 clips[num_clips++] =
\r
3861 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3865 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3866 for (i=0; i<2; i++) {
\r
3867 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3868 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3869 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3870 lastDrawnHighlight.sq[i].y >= 0) {
\r
3871 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3872 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3873 clips[num_clips++] =
\r
3874 CreateRectRgn(x - lineGap, y - lineGap,
\r
3875 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3877 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3878 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3879 clips[num_clips++] =
\r
3880 CreateRectRgn(x - lineGap, y - lineGap,
\r
3881 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3885 for (i=0; i<2; i++) {
\r
3886 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3887 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3888 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3889 lastDrawnPremove.sq[i].y >= 0) {
\r
3890 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3891 lastDrawnPremove.sq[i].x, &x, &y);
\r
3892 clips[num_clips++] =
\r
3893 CreateRectRgn(x - lineGap, y - lineGap,
\r
3894 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3896 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3897 premoveHighlightInfo.sq[i].y >= 0) {
\r
3898 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3899 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3900 clips[num_clips++] =
\r
3901 CreateRectRgn(x - lineGap, y - lineGap,
\r
3902 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3906 } else { // nr == 1
\r
3907 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3908 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3909 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3910 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3911 for (i=0; i<2; i++) {
\r
3912 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3913 partnerHighlightInfo.sq[i].y >= 0) {
\r
3914 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3915 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3916 clips[num_clips++] =
\r
3917 CreateRectRgn(x - lineGap, y - lineGap,
\r
3918 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3920 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3921 oldPartnerHighlight.sq[i].y >= 0) {
\r
3922 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3923 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3924 clips[num_clips++] =
\r
3925 CreateRectRgn(x - lineGap, y - lineGap,
\r
3926 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3931 fullrepaint = TRUE;
\r
3934 /* Create a buffer bitmap - this is the actual bitmap
\r
3935 * being written to. When all the work is done, we can
\r
3936 * copy it to the real DC (the screen). This avoids
\r
3937 * the problems with flickering.
\r
3939 GetClientRect(hwndMain, &Rect);
\r
3940 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3941 Rect.bottom-Rect.top+1);
\r
3942 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3943 if (!appData.monoMode) {
\r
3944 SelectPalette(hdcmem, hPal, FALSE);
\r
3947 /* Create clips for dragging */
\r
3948 if (!fullrepaint) {
\r
3949 if (dragInfo.from.x >= 0) {
\r
3950 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3951 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3953 if (dragInfo.start.x >= 0) {
\r
3954 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3955 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3957 if (dragInfo.pos.x >= 0) {
\r
3958 x = dragInfo.pos.x - squareSize / 2;
\r
3959 y = dragInfo.pos.y - squareSize / 2;
\r
3960 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3962 if (dragInfo.lastpos.x >= 0) {
\r
3963 x = dragInfo.lastpos.x - squareSize / 2;
\r
3964 y = dragInfo.lastpos.y - squareSize / 2;
\r
3965 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3969 /* Are we animating a move?
\r
3971 * - remove the piece from the board (temporarely)
\r
3972 * - calculate the clipping region
\r
3974 if (!fullrepaint) {
\r
3975 if (animInfo.piece != EmptySquare) {
\r
3976 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3977 x = boardRect.left + animInfo.lastpos.x;
\r
3978 y = boardRect.top + animInfo.lastpos.y;
\r
3979 x2 = boardRect.left + animInfo.pos.x;
\r
3980 y2 = boardRect.top + animInfo.pos.y;
\r
3981 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3982 /* Slight kludge. The real problem is that after AnimateMove is
\r
3983 done, the position on the screen does not match lastDrawn.
\r
3984 This currently causes trouble only on e.p. captures in
\r
3985 atomic, where the piece moves to an empty square and then
\r
3986 explodes. The old and new positions both had an empty square
\r
3987 at the destination, but animation has drawn a piece there and
\r
3988 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3990 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3994 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3995 if (num_clips == 0)
\r
3996 fullrepaint = TRUE;
\r
3998 /* Set clipping on the memory DC */
\r
3999 if (!fullrepaint) {
\r
4000 SelectClipRgn(hdcmem, clips[0]);
\r
4001 for (x = 1; x < num_clips; x++) {
\r
4002 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
4003 abort(); // this should never ever happen!
\r
4007 /* Do all the drawing to the memory DC */
\r
4008 if(explodeInfo.radius) { // [HGM] atomic
\r
4010 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
4011 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
4012 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
4013 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
4014 x += squareSize/2;
\r
4015 y += squareSize/2;
\r
4016 if(!fullrepaint) {
\r
4017 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
4018 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
4020 DrawGridOnDC(hdcmem);
\r
4021 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
4022 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
4023 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
4024 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
4025 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
4026 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
4027 SelectObject(hdcmem, oldBrush);
\r
4029 if(border) DrawBackgroundOnDC(hdcmem);
\r
4030 DrawGridOnDC(hdcmem);
\r
4031 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
4032 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
4033 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
4035 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
4036 oldPartnerHighlight = partnerHighlightInfo;
\r
4038 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
4040 if(nr == 0) // [HGM] dual: markers only on left board
\r
4041 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
4042 for (column = 0; column < BOARD_WIDTH; column++) {
\r
4043 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
4044 HBRUSH oldBrush = SelectObject(hdcmem, markerBrush[marker[row][column]-1]);
\r
4045 SquareToPos(row, column, &x, &y);
\r
4046 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
4047 x + 3*squareSize/4, y + 3*squareSize/4);
\r
4048 SelectObject(hdcmem, oldBrush);
\r
4053 if( appData.highlightMoveWithArrow ) {
\r
4055 DrawArrowHighlight(hdcmem);
\r
4058 DrawCoordsOnDC(hdcmem);
\r
4060 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
4061 /* to make sure lastDrawn contains what is actually drawn */
\r
4063 /* Put the dragged piece back into place and draw it (out of place!) */
\r
4064 if (dragged_piece != EmptySquare) {
\r
4065 /* [HGM] or restack */
\r
4066 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
4067 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
4069 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
4070 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
4072 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
4073 x = dragInfo.pos.x - squareSize / 2;
\r
4074 y = dragInfo.pos.y - squareSize / 2;
\r
4075 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
4076 ((int) dragInfo.piece < (int) BlackPawn),
\r
4077 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
4080 /* Put the animated piece back into place and draw it */
\r
4081 if (animInfo.piece != EmptySquare) {
\r
4082 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
4083 x = boardRect.left + animInfo.pos.x;
\r
4084 y = boardRect.top + animInfo.pos.y;
\r
4085 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
4086 ((int) animInfo.piece < (int) BlackPawn),
\r
4087 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
4090 /* Release the bufferBitmap by selecting in the old bitmap
\r
4091 * and delete the memory DC
\r
4093 SelectObject(hdcmem, oldBitmap);
\r
4096 /* Set clipping on the target DC */
\r
4097 if (!fullrepaint) {
\r
4098 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
4100 GetRgnBox(clips[x], &rect);
\r
4101 DeleteObject(clips[x]);
\r
4102 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
4103 rect.right + wpMain.width/2, rect.bottom);
\r
4105 SelectClipRgn(hdc, clips[0]);
\r
4106 for (x = 1; x < num_clips; x++) {
\r
4107 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
4108 abort(); // this should never ever happen!
\r
4112 /* Copy the new bitmap onto the screen in one go.
\r
4113 * This way we avoid any flickering
\r
4115 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
4116 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
4117 boardRect.right - boardRect.left,
\r
4118 boardRect.bottom - boardRect.top,
\r
4119 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
4120 if(saveDiagFlag) {
\r
4121 BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData;
\r
4122 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
4123 HBITMAP src = bufferBitmap, obmp; HDC tmp = CreateCompatibleDC(hdc);
\r
4125 bufferBitmap = CreateCompatibleBitmap(hdc, boardRect.right-boardRect.left, Rect.bottom-Rect.top-2*OUTER_MARGIN);
\r
4126 obmp = SelectObject(tmp, bufferBitmap);
\r
4127 BitBlt(tmp, 0, 0, boardRect.right - boardRect.left, Rect.bottom - Rect.top - 2*OUTER_MARGIN,
\r
4128 tmphdc, boardRect.left, OUTER_MARGIN, SRCCOPY);
\r
4129 GetObject(bufferBitmap, sizeof(b), &b);
\r
4130 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\r
4131 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
4132 bih.biWidth = b.bmWidth;
\r
4133 bih.biHeight = b.bmHeight;
\r
4135 bih.biBitCount = b.bmBitsPixel;
\r
4136 bih.biCompression = 0;
\r
4137 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
4138 bih.biXPelsPerMeter = 0;
\r
4139 bih.biYPelsPerMeter = 0;
\r
4140 bih.biClrUsed = 0;
\r
4141 bih.biClrImportant = 0;
\r
4142 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
4143 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
4144 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
4145 // fprintf(diagFile, "%8x\n", (int) pData);
\r
4147 wb = b.bmWidthBytes;
\r
4149 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
4150 int k = ((int*) pData)[i];
\r
4151 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4152 if(j >= 16) break;
\r
4154 if(j >= nrColors) nrColors = j+1;
\r
4156 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
4158 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
4159 for(w=0; w<(wb>>2); w+=2) {
\r
4160 int k = ((int*) pData)[(wb*i>>2) + w];
\r
4161 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4162 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
4163 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
4164 pData[p++] = m | j<<4;
\r
4166 while(p&3) pData[p++] = 0;
\r
4169 wb = ((wb+31)>>5)<<2;
\r
4171 // write BITMAPFILEHEADER
\r
4172 fprintf(diagFile, "BM");
\r
4173 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
4174 fputDW(diagFile, 0);
\r
4175 fputDW(diagFile, 0x36 + (fac?64:0));
\r
4176 // write BITMAPINFOHEADER
\r
4177 fputDW(diagFile, 40);
\r
4178 fputDW(diagFile, b.bmWidth);
\r
4179 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
4180 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
4181 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
4182 fputDW(diagFile, 0);
\r
4183 fputDW(diagFile, 0);
\r
4184 fputDW(diagFile, 0);
\r
4185 fputDW(diagFile, 0);
\r
4186 fputDW(diagFile, 0);
\r
4187 fputDW(diagFile, 0);
\r
4188 // write color table
\r
4190 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
4191 // write bitmap data
\r
4193 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
4194 fputc(pData[i], diagFile);
\r
4197 DeleteObject(bufferBitmap); bufferBitmap = src;
\r
4198 SelectObject(tmp, obmp);
\r
4202 SelectObject(tmphdc, oldBitmap);
\r
4204 /* Massive cleanup */
\r
4205 for (x = 0; x < num_clips; x++)
\r
4206 DeleteObject(clips[x]);
\r
4209 DeleteObject(bufferBitmap);
\r
4212 ReleaseDC(hwndMain, hdc);
\r
4214 if (lastDrawnFlipView != flipView && nr == 0) {
\r
4216 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
4218 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
4221 /* CopyBoard(lastDrawn, board);*/
\r
4222 lastDrawnHighlight = highlightInfo;
\r
4223 lastDrawnPremove = premoveHighlightInfo;
\r
4224 lastDrawnFlipView = flipView;
\r
4225 lastDrawnValid[nr] = 1;
\r
4228 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
4233 saveDiagFlag = 1; diagFile = f;
\r
4234 HDCDrawPosition(NULL, TRUE, NULL);
\r
4242 /*---------------------------------------------------------------------------*\
\r
4243 | CLIENT PAINT PROCEDURE
\r
4244 | This is the main event-handler for the WM_PAINT message.
\r
4246 \*---------------------------------------------------------------------------*/
\r
4248 PaintProc(HWND hwnd)
\r
4254 if((hdc = BeginPaint(hwnd, &ps))) {
\r
4255 if (IsIconic(hwnd)) {
\r
4256 DrawIcon(hdc, 2, 2, iconCurrent);
\r
4258 if (!appData.monoMode) {
\r
4259 SelectPalette(hdc, hPal, FALSE);
\r
4260 RealizePalette(hdc);
\r
4262 HDCDrawPosition(hdc, 1, NULL);
\r
4263 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
4264 flipView = !flipView; partnerUp = !partnerUp;
\r
4265 HDCDrawPosition(hdc, 1, NULL);
\r
4266 flipView = !flipView; partnerUp = !partnerUp;
\r
4269 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
4270 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
4271 ETO_CLIPPED|ETO_OPAQUE,
\r
4272 &messageRect, messageText, strlen(messageText), NULL);
\r
4273 SelectObject(hdc, oldFont);
\r
4274 DisplayBothClocks();
\r
4277 EndPaint(hwnd,&ps);
\r
4285 * If the user selects on a border boundary, return -1; if off the board,
\r
4286 * return -2. Otherwise map the event coordinate to the square.
\r
4287 * The offset boardRect.left or boardRect.top must already have been
\r
4288 * subtracted from x.
\r
4290 int EventToSquare(x, limit)
\r
4295 if (x < lineGap + border)
\r
4297 x -= lineGap + border;
\r
4298 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4300 x /= (squareSize + lineGap);
\r
4312 DropEnable dropEnables[] = {
\r
4313 { 'P', DP_Pawn, N_("Pawn") },
\r
4314 { 'N', DP_Knight, N_("Knight") },
\r
4315 { 'B', DP_Bishop, N_("Bishop") },
\r
4316 { 'R', DP_Rook, N_("Rook") },
\r
4317 { 'Q', DP_Queen, N_("Queen") },
\r
4321 SetupDropMenu(HMENU hmenu)
\r
4323 int i, count, enable;
\r
4325 extern char white_holding[], black_holding[];
\r
4326 char item[MSG_SIZ];
\r
4328 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4329 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4330 dropEnables[i].piece);
\r
4332 while (p && *p++ == dropEnables[i].piece) count++;
\r
4333 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4334 enable = count > 0 || !appData.testLegality
\r
4335 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4336 && !appData.icsActive);
\r
4337 ModifyMenu(hmenu, dropEnables[i].command,
\r
4338 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4339 dropEnables[i].command, item);
\r
4343 void DragPieceBegin(int x, int y, Boolean instantly)
\r
4345 dragInfo.lastpos.x = boardRect.left + x;
\r
4346 dragInfo.lastpos.y = boardRect.top + y;
\r
4347 if(instantly) dragInfo.pos = dragInfo.lastpos;
\r
4348 dragInfo.from.x = fromX;
\r
4349 dragInfo.from.y = fromY;
\r
4350 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4351 dragInfo.start = dragInfo.from;
\r
4352 SetCapture(hwndMain);
\r
4355 void DragPieceEnd(int x, int y)
\r
4358 dragInfo.start.x = dragInfo.start.y = -1;
\r
4359 dragInfo.from = dragInfo.start;
\r
4360 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4363 void ChangeDragPiece(ChessSquare piece)
\r
4365 dragInfo.piece = piece;
\r
4368 /* Event handler for mouse messages */
\r
4370 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4374 static int recursive = 0;
\r
4376 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4379 if (message == WM_MBUTTONUP) {
\r
4380 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4381 to the middle button: we simulate pressing the left button too!
\r
4383 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4384 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4390 pt.x = LOWORD(lParam);
\r
4391 pt.y = HIWORD(lParam);
\r
4392 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4393 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4394 if (!flipView && y >= 0) {
\r
4395 y = BOARD_HEIGHT - 1 - y;
\r
4397 if (flipView && x >= 0) {
\r
4398 x = BOARD_WIDTH - 1 - x;
\r
4401 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4402 controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status
\r
4404 switch (message) {
\r
4405 case WM_LBUTTONDOWN:
\r
4406 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4407 ClockClick(flipClock); break;
\r
4408 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4409 ClockClick(!flipClock); break;
\r
4411 if(dragging) { // [HGM] lion: don't destroy dragging info if we are already dragging
\r
4412 dragInfo.start.x = dragInfo.start.y = -1;
\r
4413 dragInfo.from = dragInfo.start;
\r
4415 if(fromX == -1 && frozen) { // not sure where this is for
\r
4416 fromX = fromY = -1;
\r
4417 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4420 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4421 DrawPosition(TRUE, NULL);
\r
4424 case WM_LBUTTONUP:
\r
4425 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4426 DrawPosition(TRUE, NULL);
\r
4429 case WM_MOUSEMOVE:
\r
4430 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4431 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4432 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4433 if ((appData.animateDragging || appData.highlightDragging)
\r
4434 && (wParam & MK_LBUTTON || dragging == 2)
\r
4435 && dragInfo.from.x >= 0)
\r
4437 BOOL full_repaint = FALSE;
\r
4439 if (appData.animateDragging) {
\r
4440 dragInfo.pos = pt;
\r
4442 if (appData.highlightDragging) {
\r
4443 HoverEvent(highlightInfo.sq[1].x, highlightInfo.sq[1].y, x, y);
\r
4444 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4445 full_repaint = TRUE;
\r
4449 DrawPosition( full_repaint, NULL);
\r
4451 dragInfo.lastpos = dragInfo.pos;
\r
4455 case WM_MOUSEWHEEL: // [DM]
\r
4456 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4457 /* Mouse Wheel is being rolled forward
\r
4458 * Play moves forward
\r
4460 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4461 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4462 /* Mouse Wheel is being rolled backward
\r
4463 * Play moves backward
\r
4465 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4466 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4470 case WM_MBUTTONUP:
\r
4471 case WM_RBUTTONUP:
\r
4473 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4476 case WM_MBUTTONDOWN:
\r
4477 case WM_RBUTTONDOWN:
\r
4480 fromX = fromY = -1;
\r
4481 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4482 dragInfo.start.x = dragInfo.start.y = -1;
\r
4483 dragInfo.from = dragInfo.start;
\r
4484 dragInfo.lastpos = dragInfo.pos;
\r
4485 if (appData.highlightDragging) {
\r
4486 ClearHighlights();
\r
4489 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4490 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4491 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4492 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4493 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4497 DrawPosition(TRUE, NULL);
\r
4499 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4502 if (message == WM_MBUTTONDOWN) {
\r
4503 buttonCount = 3; /* even if system didn't think so */
\r
4504 if (wParam & MK_SHIFT)
\r
4505 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4507 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4508 } else { /* message == WM_RBUTTONDOWN */
\r
4509 /* Just have one menu, on the right button. Windows users don't
\r
4510 think to try the middle one, and sometimes other software steals
\r
4511 it, or it doesn't really exist. */
\r
4512 if(gameInfo.variant != VariantShogi)
\r
4513 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4515 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4519 SetCapture(hwndMain);
\r
4522 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4523 SetupDropMenu(hmenu);
\r
4524 MenuPopup(hwnd, pt, hmenu, -1);
\r
4534 /* Preprocess messages for buttons in main window */
\r
4536 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4538 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4541 for (i=0; i<N_BUTTONS; i++) {
\r
4542 if (buttonDesc[i].id == id) break;
\r
4544 if (i == N_BUTTONS) return 0;
\r
4545 switch (message) {
\r
4550 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4551 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4558 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4561 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4562 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4563 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4564 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4566 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4568 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4569 TypeInEvent((char)wParam);
\r
4575 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4578 static int promoStyle;
\r
4580 /* Process messages for Promotion dialog box */
\r
4582 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4587 switch (message) {
\r
4589 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4590 /* Center the dialog over the application window */
\r
4591 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4592 Translate(hDlg, DLG_PromotionKing);
\r
4593 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4594 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4595 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4596 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4597 SW_SHOW : SW_HIDE);
\r
4598 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4599 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4600 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4601 PieceToChar(WhiteAngel) != '~') ||
\r
4602 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4603 PieceToChar(BlackAngel) != '~') ) ?
\r
4604 SW_SHOW : SW_HIDE);
\r
4605 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4606 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4607 PieceToChar(WhiteMarshall) != '~') ||
\r
4608 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4609 PieceToChar(BlackMarshall) != '~') ) ?
\r
4610 SW_SHOW : SW_HIDE);
\r
4611 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4612 ShowWindow(GetDlgItem(hDlg, PB_Rook), !promoStyle ? SW_SHOW : SW_HIDE);
\r
4613 ShowWindow(GetDlgItem(hDlg, PB_Bishop), !promoStyle ? SW_SHOW : SW_HIDE);
\r
4615 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4616 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4617 SetWindowText(hDlg, "Promote?");
\r
4619 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4620 gameInfo.variant == VariantSuper ?
\r
4621 SW_SHOW : SW_HIDE);
\r
4624 case WM_COMMAND: /* message: received a command */
\r
4625 switch (LOWORD(wParam)) {
\r
4627 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4628 ClearHighlights();
\r
4629 DrawPosition(FALSE, NULL);
\r
4632 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4635 promoChar = promoStyle ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4638 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4639 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4642 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4643 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4645 case PB_Chancellor:
\r
4646 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4648 case PB_Archbishop:
\r
4649 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4652 promoChar = gameInfo.variant == VariantShogi ? '=' : promoStyle ? NULLCHAR :
\r
4653 ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight));
\r
4658 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4659 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4660 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4661 fromX = fromY = -1;
\r
4662 if (!appData.highlightLastMove) {
\r
4663 ClearHighlights();
\r
4664 DrawPosition(FALSE, NULL);
\r
4671 /* Pop up promotion dialog */
\r
4673 PromotionPopup(HWND hwnd)
\r
4677 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4678 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4679 hwnd, (DLGPROC)lpProc);
\r
4680 FreeProcInstance(lpProc);
\r
4684 PromotionPopUp(char choice)
\r
4686 promoStyle = (choice == '+' || IS_SHOGI(gameInfo.variant));
\r
4687 DrawPosition(TRUE, NULL);
\r
4688 PromotionPopup(hwndMain);
\r
4692 LoadGameDialog(HWND hwnd, char* title)
\r
4696 char fileTitle[MSG_SIZ];
\r
4697 f = OpenFileDialog(hwnd, "rb", "",
\r
4698 appData.oldSaveStyle ? "gam" : "pgn",
\r
4700 title, &number, fileTitle, NULL);
\r
4702 cmailMsgLoaded = FALSE;
\r
4703 if (number == 0) {
\r
4704 int error = GameListBuild(f);
\r
4706 DisplayError(_("Cannot build game list"), error);
\r
4707 } else if (!ListEmpty(&gameList) &&
\r
4708 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4709 GameListPopUp(f, fileTitle);
\r
4712 GameListDestroy();
\r
4715 LoadGame(f, number, fileTitle, FALSE);
\r
4719 int get_term_width()
\r
4724 HFONT hfont, hold_font;
\r
4729 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4733 // get the text metrics
\r
4734 hdc = GetDC(hText);
\r
4735 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4736 if (consoleCF.dwEffects & CFE_BOLD)
\r
4737 lf.lfWeight = FW_BOLD;
\r
4738 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4739 lf.lfItalic = TRUE;
\r
4740 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4741 lf.lfStrikeOut = TRUE;
\r
4742 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4743 lf.lfUnderline = TRUE;
\r
4744 hfont = CreateFontIndirect(&lf);
\r
4745 hold_font = SelectObject(hdc, hfont);
\r
4746 GetTextMetrics(hdc, &tm);
\r
4747 SelectObject(hdc, hold_font);
\r
4748 DeleteObject(hfont);
\r
4749 ReleaseDC(hText, hdc);
\r
4751 // get the rectangle
\r
4752 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4754 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4757 void UpdateICSWidth(HWND hText)
\r
4759 LONG old_width, new_width;
\r
4761 new_width = get_term_width(hText, FALSE);
\r
4762 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4763 if (new_width != old_width)
\r
4765 ics_update_width(new_width);
\r
4766 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4771 ChangedConsoleFont()
\r
4774 CHARRANGE tmpsel, sel;
\r
4775 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4776 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4777 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4780 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4781 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4782 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4783 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4784 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4785 * size. This was undocumented in the version of MSVC++ that I had
\r
4786 * when I wrote the code, but is apparently documented now.
\r
4788 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4789 cfmt.bCharSet = f->lf.lfCharSet;
\r
4790 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4791 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4792 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4793 /* Why are the following seemingly needed too? */
\r
4794 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4795 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4796 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4798 tmpsel.cpMax = -1; /*999999?*/
\r
4799 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4800 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4801 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4802 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4804 paraf.cbSize = sizeof(paraf);
\r
4805 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4806 paraf.dxStartIndent = 0;
\r
4807 paraf.dxOffset = WRAP_INDENT;
\r
4808 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4809 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4810 UpdateICSWidth(hText);
\r
4813 /*---------------------------------------------------------------------------*\
\r
4815 * Window Proc for main window
\r
4817 \*---------------------------------------------------------------------------*/
\r
4819 /* Process messages for main window, etc. */
\r
4821 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4828 char fileTitle[MSG_SIZ];
\r
4829 static SnapData sd;
\r
4830 static int peek=0;
\r
4832 switch (message) {
\r
4834 case WM_PAINT: /* message: repaint portion of window */
\r
4838 case WM_ERASEBKGND:
\r
4839 if (IsIconic(hwnd)) {
\r
4840 /* Cheat; change the message */
\r
4841 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4843 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4847 case WM_LBUTTONDOWN:
\r
4848 case WM_MBUTTONDOWN:
\r
4849 case WM_RBUTTONDOWN:
\r
4850 case WM_LBUTTONUP:
\r
4851 case WM_MBUTTONUP:
\r
4852 case WM_RBUTTONUP:
\r
4853 case WM_MOUSEMOVE:
\r
4854 case WM_MOUSEWHEEL:
\r
4855 MouseEvent(hwnd, message, wParam, lParam);
\r
4859 if((char)wParam == '\b') {
\r
4860 ForwardEvent(); peek = 0;
\r
4863 JAWS_KBUP_NAVIGATION
\r
4868 if((char)wParam == '\b') {
\r
4869 if(!peek) BackwardEvent(), peek = 1;
\r
4872 JAWS_KBDOWN_NAVIGATION
\r
4878 JAWS_ALT_INTERCEPT
\r
4880 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4881 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4882 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4883 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4885 SendMessage(h, message, wParam, lParam);
\r
4886 } else if(lParam != KF_REPEAT) {
\r
4887 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4888 TypeInEvent((char)wParam);
\r
4889 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4890 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4895 case WM_PALETTECHANGED:
\r
4896 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4898 HDC hdc = GetDC(hwndMain);
\r
4899 SelectPalette(hdc, hPal, TRUE);
\r
4900 nnew = RealizePalette(hdc);
\r
4902 paletteChanged = TRUE;
\r
4904 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4906 ReleaseDC(hwnd, hdc);
\r
4910 case WM_QUERYNEWPALETTE:
\r
4911 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4913 HDC hdc = GetDC(hwndMain);
\r
4914 paletteChanged = FALSE;
\r
4915 SelectPalette(hdc, hPal, FALSE);
\r
4916 nnew = RealizePalette(hdc);
\r
4918 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4920 ReleaseDC(hwnd, hdc);
\r
4925 case WM_COMMAND: /* message: command from application menu */
\r
4926 wmId = LOWORD(wParam);
\r
4931 SAY("new game enter a move to play against the computer with white");
\r
4934 case IDM_NewGameFRC:
\r
4935 if( NewGameFRC() == 0 ) {
\r
4940 case IDM_NewVariant:
\r
4941 NewVariantPopup(hwnd);
\r
4944 case IDM_LoadGame:
\r
4945 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4948 case IDM_LoadNextGame:
\r
4952 case IDM_LoadPrevGame:
\r
4956 case IDM_ReloadGame:
\r
4960 case IDM_LoadPosition:
\r
4961 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4962 Reset(FALSE, TRUE);
\r
4965 f = OpenFileDialog(hwnd, "rb", "",
\r
4966 appData.oldSaveStyle ? "pos" : "fen",
\r
4968 _("Load Position from File"), &number, fileTitle, NULL);
\r
4970 LoadPosition(f, number, fileTitle);
\r
4974 case IDM_LoadNextPosition:
\r
4975 ReloadPosition(1);
\r
4978 case IDM_LoadPrevPosition:
\r
4979 ReloadPosition(-1);
\r
4982 case IDM_ReloadPosition:
\r
4983 ReloadPosition(0);
\r
4986 case IDM_SaveGame:
\r
4987 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4988 f = OpenFileDialog(hwnd, "a", defName,
\r
4989 appData.oldSaveStyle ? "gam" : "pgn",
\r
4991 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4993 SaveGame(f, 0, "");
\r
4997 case IDM_SavePosition:
\r
4998 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4999 f = OpenFileDialog(hwnd, "a", defName,
\r
5000 appData.oldSaveStyle ? "pos" : "fen",
\r
5002 _("Save Position to File"), NULL, fileTitle, NULL);
\r
5004 SavePosition(f, 0, "");
\r
5008 case IDM_SaveDiagram:
\r
5009 defName = "diagram";
\r
5010 f = OpenFileDialog(hwnd, "wb", defName,
\r
5013 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
5019 case IDM_SaveSelected:
\r
5020 f = OpenFileDialog(hwnd, "a", "",
\r
5023 _("Save Game to File"), NULL, fileTitle, NULL);
\r
5025 SaveSelected(f, 0, "");
\r
5029 case IDM_CreateBook:
\r
5030 CreateBookEvent();
\r
5033 case IDM_CopyGame:
\r
5034 CopyGameToClipboard();
\r
5037 case IDM_PasteGame:
\r
5038 PasteGameFromClipboard();
\r
5041 case IDM_CopyGameListToClipboard:
\r
5042 CopyGameListToClipboard();
\r
5045 /* [AS] Autodetect FEN or PGN data */
\r
5046 case IDM_PasteAny:
\r
5047 PasteGameOrFENFromClipboard();
\r
5050 /* [AS] Move history */
\r
5051 case IDM_ShowMoveHistory:
\r
5052 if( MoveHistoryIsUp() ) {
\r
5053 MoveHistoryPopDown();
\r
5056 MoveHistoryPopUp();
\r
5060 /* [AS] Eval graph */
\r
5061 case IDM_ShowEvalGraph:
\r
5062 if( EvalGraphIsUp() ) {
\r
5063 EvalGraphPopDown();
\r
5067 SetFocus(hwndMain);
\r
5071 /* [AS] Engine output */
\r
5072 case IDM_ShowEngineOutput:
\r
5073 if( EngineOutputIsUp() ) {
\r
5074 EngineOutputPopDown();
\r
5077 EngineOutputPopUp();
\r
5081 /* [AS] User adjudication */
\r
5082 case IDM_UserAdjudication_White:
\r
5083 UserAdjudicationEvent( +1 );
\r
5086 case IDM_UserAdjudication_Black:
\r
5087 UserAdjudicationEvent( -1 );
\r
5090 case IDM_UserAdjudication_Draw:
\r
5091 UserAdjudicationEvent( 0 );
\r
5094 /* [AS] Game list options dialog */
\r
5095 case IDM_GameListOptions:
\r
5096 GameListOptions();
\r
5103 case IDM_CopyPosition:
\r
5104 CopyFENToClipboard();
\r
5107 case IDM_PastePosition:
\r
5108 PasteFENFromClipboard();
\r
5111 case IDM_MailMove:
\r
5115 case IDM_ReloadCMailMsg:
\r
5116 Reset(TRUE, TRUE);
\r
5117 ReloadCmailMsgEvent(FALSE);
\r
5120 case IDM_Minimize:
\r
5121 ShowWindow(hwnd, SW_MINIMIZE);
\r
5128 case IDM_MachineWhite:
\r
5129 MachineWhiteEvent();
\r
5131 * refresh the tags dialog only if it's visible
\r
5133 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
5135 tags = PGNTags(&gameInfo);
\r
5136 TagsPopUp(tags, CmailMsg());
\r
5139 SAY("computer starts playing white");
\r
5142 case IDM_MachineBlack:
\r
5143 MachineBlackEvent();
\r
5145 * refresh the tags dialog only if it's visible
\r
5147 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
5149 tags = PGNTags(&gameInfo);
\r
5150 TagsPopUp(tags, CmailMsg());
\r
5153 SAY("computer starts playing black");
\r
5156 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
5157 if(matchMode) EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_GRAYED);
\r
5158 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
5161 case IDM_TwoMachines:
\r
5162 TwoMachinesEvent();
\r
5165 * refresh the tags dialog only if it's visible
\r
5167 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
5169 tags = PGNTags(&gameInfo);
\r
5170 TagsPopUp(tags, CmailMsg());
\r
5173 SAY("computer starts playing both sides");
\r
5176 case IDM_AnalysisMode:
\r
5177 if(AnalyzeModeEvent()) {
\r
5178 SAY("analyzing current position");
\r
5182 case IDM_AnalyzeFile:
\r
5183 AnalyzeFileEvent();
\r
5186 case IDM_IcsClient:
\r
5190 case IDM_EditGame:
\r
5191 case IDM_EditGame2:
\r
5196 case IDM_EditPosition:
\r
5197 case IDM_EditPosition2:
\r
5198 EditPositionEvent();
\r
5199 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
5202 case IDM_Training:
\r
5206 case IDM_ShowGameList:
\r
5207 ShowGameListProc();
\r
5210 case IDM_EditProgs1:
\r
5211 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
5214 case IDM_LoadProg1:
\r
5215 LoadEnginePopUp(hwndMain, 0);
\r
5218 case IDM_LoadProg2:
\r
5219 LoadEnginePopUp(hwndMain, 1);
\r
5222 case IDM_EditServers:
\r
5223 EditTagsPopUp(icsNames, &icsNames);
\r
5226 case IDM_EditTags:
\r
5231 case IDM_EditBook:
\r
5235 case IDM_EditComment:
\r
5237 if (commentUp && editComment) {
\r
5240 EditCommentEvent();
\r
5261 case IDM_CallFlag:
\r
5281 case IDM_StopObserving:
\r
5282 StopObservingEvent();
\r
5285 case IDM_StopExamining:
\r
5286 StopExaminingEvent();
\r
5290 UploadGameEvent();
\r
5293 case IDM_TypeInMove:
\r
5294 TypeInEvent('\000');
\r
5297 case IDM_TypeInName:
\r
5298 PopUpNameDialog('\000');
\r
5301 case IDM_Backward:
\r
5303 SetFocus(hwndMain);
\r
5310 SetFocus(hwndMain);
\r
5315 SetFocus(hwndMain);
\r
5320 SetFocus(hwndMain);
\r
5323 case OPT_GameListNext: // [HGM] forward these two accelerators to Game List
\r
5324 case OPT_GameListPrev:
\r
5325 if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);
\r
5329 RevertEvent(FALSE);
\r
5332 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5333 RevertEvent(TRUE);
\r
5336 case IDM_TruncateGame:
\r
5337 TruncateGameEvent();
\r
5344 case IDM_RetractMove:
\r
5345 RetractMoveEvent();
\r
5348 case IDM_FlipView:
\r
5349 flipView = !flipView;
\r
5350 DrawPosition(FALSE, NULL);
\r
5353 case IDM_FlipClock:
\r
5354 flipClock = !flipClock;
\r
5355 DisplayBothClocks();
\r
5359 case IDM_MuteSounds:
\r
5360 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5361 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5362 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5365 case IDM_GeneralOptions:
\r
5366 GeneralOptionsPopup(hwnd);
\r
5367 DrawPosition(TRUE, NULL);
\r
5370 case IDM_BoardOptions:
\r
5371 BoardOptionsPopup(hwnd);
\r
5374 case IDM_ThemeOptions:
\r
5375 ThemeOptionsPopup(hwnd);
\r
5378 case IDM_EnginePlayOptions:
\r
5379 EnginePlayOptionsPopup(hwnd);
\r
5382 case IDM_Engine1Options:
\r
5383 EngineOptionsPopup(hwnd, &first);
\r
5386 case IDM_Engine2Options:
\r
5388 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5389 EngineOptionsPopup(hwnd, &second);
\r
5392 case IDM_OptionsUCI:
\r
5393 UciOptionsPopup(hwnd);
\r
5397 TourneyPopup(hwnd);
\r
5400 case IDM_IcsOptions:
\r
5401 IcsOptionsPopup(hwnd);
\r
5405 FontsOptionsPopup(hwnd);
\r
5409 SoundOptionsPopup(hwnd);
\r
5412 case IDM_CommPort:
\r
5413 CommPortOptionsPopup(hwnd);
\r
5416 case IDM_LoadOptions:
\r
5417 LoadOptionsPopup(hwnd);
\r
5420 case IDM_SaveOptions:
\r
5421 SaveOptionsPopup(hwnd);
\r
5424 case IDM_TimeControl:
\r
5425 TimeControlOptionsPopup(hwnd);
\r
5428 case IDM_SaveSettings:
\r
5429 SaveSettings(settingsFileName);
\r
5432 case IDM_SaveSettingsOnExit:
\r
5433 saveSettingsOnExit = !saveSettingsOnExit;
\r
5434 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5435 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5436 MF_CHECKED : MF_UNCHECKED));
\r
5447 case IDM_AboutGame:
\r
5452 appData.debugMode = !appData.debugMode;
\r
5453 if (appData.debugMode) {
\r
5454 char dir[MSG_SIZ];
\r
5455 GetCurrentDirectory(MSG_SIZ, dir);
\r
5456 SetCurrentDirectory(installDir);
\r
5457 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5458 SetCurrentDirectory(dir);
\r
5459 setbuf(debugFP, NULL);
\r
5466 case IDM_HELPCONTENTS:
\r
5467 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5468 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5469 MessageBox (GetFocus(),
\r
5470 _("Unable to activate help"),
\r
5471 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5475 case IDM_HELPSEARCH:
\r
5476 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5477 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5478 MessageBox (GetFocus(),
\r
5479 _("Unable to activate help"),
\r
5480 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5484 case IDM_HELPHELP:
\r
5485 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5486 MessageBox (GetFocus(),
\r
5487 _("Unable to activate help"),
\r
5488 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5493 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5495 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5496 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5497 FreeProcInstance(lpProc);
\r
5500 case IDM_DirectCommand1:
\r
5501 AskQuestionEvent(_("Direct Command"),
\r
5502 _("Send to chess program:"), "", "1");
\r
5504 case IDM_DirectCommand2:
\r
5505 AskQuestionEvent(_("Direct Command"),
\r
5506 _("Send to second chess program:"), "", "2");
\r
5509 case EP_WhitePawn:
\r
5510 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5511 fromX = fromY = -1;
\r
5514 case EP_WhiteKnight:
\r
5515 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5516 fromX = fromY = -1;
\r
5519 case EP_WhiteBishop:
\r
5520 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5521 fromX = fromY = -1;
\r
5524 case EP_WhiteRook:
\r
5525 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5526 fromX = fromY = -1;
\r
5529 case EP_WhiteQueen:
\r
5530 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5531 fromX = fromY = -1;
\r
5534 case EP_WhiteFerz:
\r
5535 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5536 fromX = fromY = -1;
\r
5539 case EP_WhiteWazir:
\r
5540 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5541 fromX = fromY = -1;
\r
5544 case EP_WhiteAlfil:
\r
5545 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5546 fromX = fromY = -1;
\r
5549 case EP_WhiteCannon:
\r
5550 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5551 fromX = fromY = -1;
\r
5554 case EP_WhiteCardinal:
\r
5555 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5556 fromX = fromY = -1;
\r
5559 case EP_WhiteMarshall:
\r
5560 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5561 fromX = fromY = -1;
\r
5564 case EP_WhiteKing:
\r
5565 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5566 fromX = fromY = -1;
\r
5569 case EP_BlackPawn:
\r
5570 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5571 fromX = fromY = -1;
\r
5574 case EP_BlackKnight:
\r
5575 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5576 fromX = fromY = -1;
\r
5579 case EP_BlackBishop:
\r
5580 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5581 fromX = fromY = -1;
\r
5584 case EP_BlackRook:
\r
5585 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5586 fromX = fromY = -1;
\r
5589 case EP_BlackQueen:
\r
5590 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5591 fromX = fromY = -1;
\r
5594 case EP_BlackFerz:
\r
5595 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5596 fromX = fromY = -1;
\r
5599 case EP_BlackWazir:
\r
5600 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5601 fromX = fromY = -1;
\r
5604 case EP_BlackAlfil:
\r
5605 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5606 fromX = fromY = -1;
\r
5609 case EP_BlackCannon:
\r
5610 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5611 fromX = fromY = -1;
\r
5614 case EP_BlackCardinal:
\r
5615 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5616 fromX = fromY = -1;
\r
5619 case EP_BlackMarshall:
\r
5620 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5621 fromX = fromY = -1;
\r
5624 case EP_BlackKing:
\r
5625 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5626 fromX = fromY = -1;
\r
5629 case EP_EmptySquare:
\r
5630 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5631 fromX = fromY = -1;
\r
5634 case EP_ClearBoard:
\r
5635 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5636 fromX = fromY = -1;
\r
5640 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5641 fromX = fromY = -1;
\r
5645 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5646 fromX = fromY = -1;
\r
5650 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5651 fromX = fromY = -1;
\r
5655 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5656 fromX = fromY = -1;
\r
5660 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5661 fromX = fromY = -1;
\r
5665 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5666 fromX = fromY = -1;
\r
5670 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5671 fromX = fromY = -1;
\r
5675 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5676 fromX = fromY = -1;
\r
5680 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5681 fromX = fromY = -1;
\r
5685 barbaric = 0; appData.language = "";
\r
5686 TranslateMenus(0);
\r
5687 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5688 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5689 lastChecked = wmId;
\r
5693 if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)
\r
5694 RecentEngineEvent(wmId - IDM_RecentEngines);
\r
5696 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5697 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5698 TranslateMenus(0);
\r
5699 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5700 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5701 lastChecked = wmId;
\r
5704 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5710 case CLOCK_TIMER_ID:
\r
5711 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5712 clockTimerEvent = 0;
\r
5713 DecrementClocks(); /* call into back end */
\r
5715 case LOAD_GAME_TIMER_ID:
\r
5716 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5717 loadGameTimerEvent = 0;
\r
5718 AutoPlayGameLoop(); /* call into back end */
\r
5720 case ANALYSIS_TIMER_ID:
\r
5721 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5722 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5723 AnalysisPeriodicEvent(0);
\r
5725 KillTimer(hwnd, analysisTimerEvent);
\r
5726 analysisTimerEvent = 0;
\r
5729 case DELAYED_TIMER_ID:
\r
5730 KillTimer(hwnd, delayedTimerEvent);
\r
5731 delayedTimerEvent = 0;
\r
5732 delayedTimerCallback();
\r
5737 case WM_USER_Input:
\r
5738 InputEvent(hwnd, message, wParam, lParam);
\r
5741 /* [AS] Also move "attached" child windows */
\r
5742 case WM_WINDOWPOSCHANGING:
\r
5744 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5745 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5747 if( ((lpwp->flags & SWP_NOMOVE) == 0) /*&& ((lpwp->flags & SWP_NOSIZE) != 0)*/ ) { // [HGM] in Win8 size always accompanies move?
\r
5748 /* Window is moving */
\r
5751 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5752 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5753 rcMain.right = wpMain.x + wpMain.width;
\r
5754 rcMain.top = wpMain.y;
\r
5755 rcMain.bottom = wpMain.y + wpMain.height;
\r
5757 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5758 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5759 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5760 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5761 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5762 wpMain.x = lpwp->x;
\r
5763 wpMain.y = lpwp->y;
\r
5769 /* [AS] Snapping */
\r
5770 case WM_ENTERSIZEMOVE:
\r
5771 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5772 if (hwnd == hwndMain) {
\r
5773 doingSizing = TRUE;
\r
5776 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5780 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5781 if (hwnd == hwndMain) {
\r
5782 lastSizing = wParam;
\r
5787 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5788 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5790 case WM_EXITSIZEMOVE:
\r
5791 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5792 if (hwnd == hwndMain) {
\r
5794 doingSizing = FALSE;
\r
5795 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5796 GetClientRect(hwnd, &client);
\r
5797 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5799 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5801 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5804 case WM_DESTROY: /* message: window being destroyed */
\r
5805 PostQuitMessage(0);
\r
5809 if (hwnd == hwndMain) {
\r
5814 default: /* Passes it on if unprocessed */
\r
5815 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5822 /*---------------------------------------------------------------------------*\
\r
5824 * Misc utility routines
\r
5826 \*---------------------------------------------------------------------------*/
\r
5829 * Decent random number generator, at least not as bad as Windows
\r
5830 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5832 unsigned int randstate;
\r
5837 randstate = randstate * 1664525 + 1013904223;
\r
5838 return (int) randstate & 0x7fffffff;
\r
5842 mysrandom(unsigned int seed)
\r
5849 * returns TRUE if user selects a different color, FALSE otherwise
\r
5853 ChangeColor(HWND hwnd, COLORREF *which)
\r
5855 static BOOL firstTime = TRUE;
\r
5856 static DWORD customColors[16];
\r
5858 COLORREF newcolor;
\r
5863 /* Make initial colors in use available as custom colors */
\r
5864 /* Should we put the compiled-in defaults here instead? */
\r
5866 customColors[i++] = lightSquareColor & 0xffffff;
\r
5867 customColors[i++] = darkSquareColor & 0xffffff;
\r
5868 customColors[i++] = whitePieceColor & 0xffffff;
\r
5869 customColors[i++] = blackPieceColor & 0xffffff;
\r
5870 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5871 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5873 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5874 customColors[i++] = textAttribs[ccl].color;
\r
5876 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5877 firstTime = FALSE;
\r
5880 cc.lStructSize = sizeof(cc);
\r
5881 cc.hwndOwner = hwnd;
\r
5882 cc.hInstance = NULL;
\r
5883 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5884 cc.lpCustColors = (LPDWORD) customColors;
\r
5885 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5887 if (!ChooseColor(&cc)) return FALSE;
\r
5889 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5890 if (newcolor == *which) return FALSE;
\r
5891 *which = newcolor;
\r
5895 InitDrawingColors();
\r
5896 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5901 MyLoadSound(MySound *ms)
\r
5907 if (ms->data && ms->flag) free(ms->data);
\r
5910 switch (ms->name[0]) {
\r
5916 /* System sound from Control Panel. Don't preload here. */
\r
5920 if (ms->name[1] == NULLCHAR) {
\r
5921 /* "!" alone = silence */
\r
5924 /* Builtin wave resource. Error if not found. */
\r
5925 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5926 if (h == NULL) break;
\r
5927 ms->data = (void *)LoadResource(hInst, h);
\r
5928 ms->flag = 0; // not maloced, so cannot be freed!
\r
5929 if (h == NULL) break;
\r
5934 /* .wav file. Error if not found. */
\r
5935 f = fopen(ms->name, "rb");
\r
5936 if (f == NULL) break;
\r
5937 if (fstat(fileno(f), &st) < 0) break;
\r
5938 ms->data = malloc(st.st_size);
\r
5940 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5946 char buf[MSG_SIZ];
\r
5947 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5948 DisplayError(buf, GetLastError());
\r
5954 MyPlaySound(MySound *ms)
\r
5956 BOOLEAN ok = FALSE;
\r
5958 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5959 switch (ms->name[0]) {
\r
5961 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5966 /* System sound from Control Panel (deprecated feature).
\r
5967 "$" alone or an unset sound name gets default beep (still in use). */
\r
5968 if (ms->name[1]) {
\r
5969 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5971 if (!ok) ok = MessageBeep(MB_OK);
\r
5974 /* Builtin wave resource, or "!" alone for silence */
\r
5975 if (ms->name[1]) {
\r
5976 if (ms->data == NULL) return FALSE;
\r
5977 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5983 /* .wav file. Error if not found. */
\r
5984 if (ms->data == NULL) return FALSE;
\r
5985 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5988 /* Don't print an error: this can happen innocently if the sound driver
\r
5989 is busy; for instance, if another instance of WinBoard is playing
\r
5990 a sound at about the same time. */
\r
5996 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5999 OPENFILENAME *ofn;
\r
6000 static UINT *number; /* gross that this is static */
\r
6002 switch (message) {
\r
6003 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6004 /* Center the dialog over the application window */
\r
6005 ofn = (OPENFILENAME *) lParam;
\r
6006 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
6007 number = (UINT *) ofn->lCustData;
\r
6008 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
6012 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6013 Translate(hDlg, 1536);
\r
6014 return FALSE; /* Allow for further processing */
\r
6017 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
6018 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
6020 return FALSE; /* Allow for further processing */
\r
6026 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
6028 static UINT *number;
\r
6029 OPENFILENAME *ofname;
\r
6032 case WM_INITDIALOG:
\r
6033 Translate(hdlg, DLG_IndexNumber);
\r
6034 ofname = (OPENFILENAME *)lParam;
\r
6035 number = (UINT *)(ofname->lCustData);
\r
6038 ofnot = (OFNOTIFY *)lParam;
\r
6039 if (ofnot->hdr.code == CDN_FILEOK) {
\r
6040 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
6049 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
6050 char *nameFilt, char *dlgTitle, UINT *number,
\r
6051 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
6053 OPENFILENAME openFileName;
\r
6054 char buf1[MSG_SIZ];
\r
6057 if (fileName == NULL) fileName = buf1;
\r
6058 if (defName == NULL) {
\r
6059 safeStrCpy(fileName, "*.", 3 );
\r
6060 strcat(fileName, defExt);
\r
6062 safeStrCpy(fileName, defName, MSG_SIZ );
\r
6064 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
6065 if (number) *number = 0;
\r
6067 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
6068 openFileName.hwndOwner = hwnd;
\r
6069 openFileName.hInstance = (HANDLE) hInst;
\r
6070 openFileName.lpstrFilter = nameFilt;
\r
6071 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
6072 openFileName.nMaxCustFilter = 0L;
\r
6073 openFileName.nFilterIndex = 1L;
\r
6074 openFileName.lpstrFile = fileName;
\r
6075 openFileName.nMaxFile = MSG_SIZ;
\r
6076 openFileName.lpstrFileTitle = fileTitle;
\r
6077 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
6078 openFileName.lpstrInitialDir = NULL;
\r
6079 openFileName.lpstrTitle = dlgTitle;
\r
6080 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
6081 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
6082 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
6083 | (oldDialog ? 0 : OFN_EXPLORER);
\r
6084 openFileName.nFileOffset = 0;
\r
6085 openFileName.nFileExtension = 0;
\r
6086 openFileName.lpstrDefExt = defExt;
\r
6087 openFileName.lCustData = (LONG) number;
\r
6088 openFileName.lpfnHook = oldDialog ?
\r
6089 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
6090 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
6092 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
6093 GetOpenFileName(&openFileName)) {
\r
6094 /* open the file */
\r
6095 f = fopen(openFileName.lpstrFile, write);
\r
6097 MessageBox(hwnd, _("File open failed"), NULL,
\r
6098 MB_OK|MB_ICONEXCLAMATION);
\r
6102 int err = CommDlgExtendedError();
\r
6103 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
6112 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
6114 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
6117 * Get the first pop-up menu in the menu template. This is the
\r
6118 * menu that TrackPopupMenu displays.
\r
6120 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
6121 TranslateOneMenu(10, hmenuTrackPopup);
\r
6123 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
6126 * TrackPopup uses screen coordinates, so convert the
\r
6127 * coordinates of the mouse click to screen coordinates.
\r
6129 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
6131 /* Draw and track the floating pop-up menu. */
\r
6132 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
6133 pt.x, pt.y, 0, hwnd, NULL);
\r
6135 /* Destroy the menu.*/
\r
6136 DestroyMenu(hmenu);
\r
6141 int sizeX, sizeY, newSizeX, newSizeY;
\r
6143 } ResizeEditPlusButtonsClosure;
\r
6146 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
6148 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
6152 if (hChild == cl->hText) return TRUE;
\r
6153 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
6154 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
6155 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
6156 ScreenToClient(cl->hDlg, &pt);
\r
6157 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
6158 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
6162 /* Resize a dialog that has a (rich) edit field filling most of
\r
6163 the top, with a row of buttons below */
\r
6165 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
6168 int newTextHeight, newTextWidth;
\r
6169 ResizeEditPlusButtonsClosure cl;
\r
6171 /*if (IsIconic(hDlg)) return;*/
\r
6172 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
6174 cl.hdwp = BeginDeferWindowPos(8);
\r
6176 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
6177 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6178 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6179 if (newTextHeight < 0) {
\r
6180 newSizeY += -newTextHeight;
\r
6181 newTextHeight = 0;
\r
6183 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
6184 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6190 cl.newSizeX = newSizeX;
\r
6191 cl.newSizeY = newSizeY;
\r
6192 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
6194 EndDeferWindowPos(cl.hdwp);
\r
6197 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
6199 RECT rChild, rParent;
\r
6200 int wChild, hChild, wParent, hParent;
\r
6201 int wScreen, hScreen, xNew, yNew;
\r
6204 /* Get the Height and Width of the child window */
\r
6205 GetWindowRect (hwndChild, &rChild);
\r
6206 wChild = rChild.right - rChild.left;
\r
6207 hChild = rChild.bottom - rChild.top;
\r
6209 /* Get the Height and Width of the parent window */
\r
6210 GetWindowRect (hwndParent, &rParent);
\r
6211 wParent = rParent.right - rParent.left;
\r
6212 hParent = rParent.bottom - rParent.top;
\r
6214 /* Get the display limits */
\r
6215 hdc = GetDC (hwndChild);
\r
6216 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
6217 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
6218 ReleaseDC(hwndChild, hdc);
\r
6220 /* Calculate new X position, then adjust for screen */
\r
6221 xNew = rParent.left + ((wParent - wChild) /2);
\r
6224 } else if ((xNew+wChild) > wScreen) {
\r
6225 xNew = wScreen - wChild;
\r
6228 /* Calculate new Y position, then adjust for screen */
\r
6230 yNew = rParent.top + ((hParent - hChild) /2);
\r
6233 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
6238 } else if ((yNew+hChild) > hScreen) {
\r
6239 yNew = hScreen - hChild;
\r
6242 /* Set it, and return */
\r
6243 return SetWindowPos (hwndChild, NULL,
\r
6244 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
6247 /* Center one window over another */
\r
6248 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
6250 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
6253 /*---------------------------------------------------------------------------*\
\r
6255 * Startup Dialog functions
\r
6257 \*---------------------------------------------------------------------------*/
\r
6259 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
6261 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6263 while (*cd != NULL) {
\r
6264 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
6270 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
6272 char buf1[MAX_ARG_LEN];
\r
6275 if (str[0] == '@') {
\r
6276 FILE* f = fopen(str + 1, "r");
\r
6278 DisplayFatalError(str + 1, errno, 2);
\r
6281 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
6283 buf1[len] = NULLCHAR;
\r
6289 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6292 char buf[MSG_SIZ];
\r
6293 char *end = strchr(str, '\n');
\r
6294 if (end == NULL) return;
\r
6295 memcpy(buf, str, end - str);
\r
6296 buf[end - str] = NULLCHAR;
\r
6297 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6303 SetStartupDialogEnables(HWND hDlg)
\r
6305 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6306 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6307 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6308 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6309 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6310 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6311 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6312 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6313 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6314 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6315 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6316 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6317 IsDlgButtonChecked(hDlg, OPT_View));
\r
6321 QuoteForFilename(char *filename)
\r
6323 int dquote, space;
\r
6324 dquote = strchr(filename, '"') != NULL;
\r
6325 space = strchr(filename, ' ') != NULL;
\r
6326 if (dquote || space) {
\r
6338 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6340 char buf[MSG_SIZ];
\r
6343 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6344 q = QuoteForFilename(nthcp);
\r
6345 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6346 if (*nthdir != NULLCHAR) {
\r
6347 q = QuoteForFilename(nthdir);
\r
6348 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6350 if (*nthcp == NULLCHAR) {
\r
6351 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6352 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6353 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6354 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6359 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6361 char buf[MSG_SIZ];
\r
6365 switch (message) {
\r
6366 case WM_INITDIALOG:
\r
6367 /* Center the dialog */
\r
6368 CenterWindow (hDlg, GetDesktopWindow());
\r
6369 Translate(hDlg, DLG_Startup);
\r
6370 /* Initialize the dialog items */
\r
6371 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6372 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6373 firstChessProgramNames);
\r
6374 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6375 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6376 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6377 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6378 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6379 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6380 if (*appData.icsHelper != NULLCHAR) {
\r
6381 char *q = QuoteForFilename(appData.icsHelper);
\r
6382 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6384 if (*appData.icsHost == NULLCHAR) {
\r
6385 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6386 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6387 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6388 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6389 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6392 if (appData.icsActive) {
\r
6393 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6395 else if (appData.noChessProgram) {
\r
6396 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6399 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6402 SetStartupDialogEnables(hDlg);
\r
6406 switch (LOWORD(wParam)) {
\r
6408 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6409 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6410 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6412 comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox
\r
6413 ParseArgs(StringGet, &p);
\r
6414 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6415 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6417 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6418 ParseArgs(StringGet, &p);
\r
6419 SwapEngines(singleList); // ... and then make it 'second'
\r
6421 appData.noChessProgram = FALSE;
\r
6422 appData.icsActive = FALSE;
\r
6423 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6424 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6425 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6427 ParseArgs(StringGet, &p);
\r
6428 if (appData.zippyPlay) {
\r
6429 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6430 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6432 ParseArgs(StringGet, &p);
\r
6434 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6435 appData.noChessProgram = TRUE;
\r
6436 appData.icsActive = FALSE;
\r
6438 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6439 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6442 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6443 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6445 ParseArgs(StringGet, &p);
\r
6447 EndDialog(hDlg, TRUE);
\r
6454 case IDM_HELPCONTENTS:
\r
6455 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6456 MessageBox (GetFocus(),
\r
6457 _("Unable to activate help"),
\r
6458 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6463 SetStartupDialogEnables(hDlg);
\r
6471 /*---------------------------------------------------------------------------*\
\r
6473 * About box dialog functions
\r
6475 \*---------------------------------------------------------------------------*/
\r
6477 /* Process messages for "About" dialog box */
\r
6479 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6481 switch (message) {
\r
6482 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6483 /* Center the dialog over the application window */
\r
6484 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6485 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6486 Translate(hDlg, ABOUTBOX);
\r
6490 case WM_COMMAND: /* message: received a command */
\r
6491 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6492 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6493 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6501 /*---------------------------------------------------------------------------*\
\r
6503 * Comment Dialog functions
\r
6505 \*---------------------------------------------------------------------------*/
\r
6508 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6510 static HANDLE hwndText = NULL;
\r
6511 int len, newSizeX, newSizeY;
\r
6512 static int sizeX, sizeY;
\r
6517 switch (message) {
\r
6518 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6519 /* Initialize the dialog items */
\r
6520 Translate(hDlg, DLG_EditComment);
\r
6521 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6522 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6523 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6524 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6525 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6526 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6527 SetWindowText(hDlg, commentTitle);
\r
6528 if (editComment) {
\r
6529 SetFocus(hwndText);
\r
6531 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6533 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6534 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6535 MAKELPARAM(FALSE, 0));
\r
6536 /* Size and position the dialog */
\r
6537 if (!commentDialog) {
\r
6538 commentDialog = hDlg;
\r
6539 GetClientRect(hDlg, &rect);
\r
6540 sizeX = rect.right;
\r
6541 sizeY = rect.bottom;
\r
6542 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6543 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6544 WINDOWPLACEMENT wp;
\r
6545 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6546 wp.length = sizeof(WINDOWPLACEMENT);
\r
6548 wp.showCmd = SW_SHOW;
\r
6549 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6550 wp.rcNormalPosition.left = wpComment.x;
\r
6551 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6552 wp.rcNormalPosition.top = wpComment.y;
\r
6553 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6554 SetWindowPlacement(hDlg, &wp);
\r
6556 GetClientRect(hDlg, &rect);
\r
6557 newSizeX = rect.right;
\r
6558 newSizeY = rect.bottom;
\r
6559 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6560 newSizeX, newSizeY);
\r
6565 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6568 case WM_COMMAND: /* message: received a command */
\r
6569 switch (LOWORD(wParam)) {
\r
6571 if (editComment) {
\r
6573 /* Read changed options from the dialog box */
\r
6574 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6575 len = GetWindowTextLength(hwndText);
\r
6576 str = (char *) malloc(len + 1);
\r
6577 GetWindowText(hwndText, str, len + 1);
\r
6586 ReplaceComment(commentIndex, str);
\r
6593 case OPT_CancelComment:
\r
6597 case OPT_ClearComment:
\r
6598 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6601 case OPT_EditComment:
\r
6602 EditCommentEvent();
\r
6610 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6611 if( wParam == OPT_CommentText ) {
\r
6612 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6614 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6615 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6619 pt.x = LOWORD( lpMF->lParam );
\r
6620 pt.y = HIWORD( lpMF->lParam );
\r
6622 if(lpMF->msg == WM_CHAR) {
\r
6624 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6625 index = sel.cpMin;
\r
6627 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6629 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6630 len = GetWindowTextLength(hwndText);
\r
6631 str = (char *) malloc(len + 1);
\r
6632 GetWindowText(hwndText, str, len + 1);
\r
6633 ReplaceComment(commentIndex, str);
\r
6634 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6635 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6638 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6639 lpMF->msg = WM_USER;
\r
6647 newSizeX = LOWORD(lParam);
\r
6648 newSizeY = HIWORD(lParam);
\r
6649 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6654 case WM_GETMINMAXINFO:
\r
6655 /* Prevent resizing window too small */
\r
6656 mmi = (MINMAXINFO *) lParam;
\r
6657 mmi->ptMinTrackSize.x = 100;
\r
6658 mmi->ptMinTrackSize.y = 100;
\r
6665 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6670 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6672 if (str == NULL) str = "";
\r
6673 p = (char *) malloc(2 * strlen(str) + 2);
\r
6676 if (*str == '\n') *q++ = '\r';
\r
6680 if (commentText != NULL) free(commentText);
\r
6682 commentIndex = index;
\r
6683 commentTitle = title;
\r
6685 editComment = edit;
\r
6687 if (commentDialog) {
\r
6688 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6689 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6691 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6692 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6693 hwndMain, (DLGPROC)lpProc);
\r
6694 FreeProcInstance(lpProc);
\r
6700 /*---------------------------------------------------------------------------*\
\r
6702 * Type-in move dialog functions
\r
6704 \*---------------------------------------------------------------------------*/
\r
6707 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6709 char move[MSG_SIZ];
\r
6712 switch (message) {
\r
6713 case WM_INITDIALOG:
\r
6714 move[0] = (char) lParam;
\r
6715 move[1] = NULLCHAR;
\r
6716 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6717 Translate(hDlg, DLG_TypeInMove);
\r
6718 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6719 SetWindowText(hInput, move);
\r
6721 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6725 switch (LOWORD(wParam)) {
\r
6728 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6729 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6730 TypeInDoneEvent(move);
\r
6731 EndDialog(hDlg, TRUE);
\r
6734 EndDialog(hDlg, FALSE);
\r
6745 PopUpMoveDialog(char firstchar)
\r
6749 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6750 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6751 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6752 FreeProcInstance(lpProc);
\r
6755 /*---------------------------------------------------------------------------*\
\r
6757 * Type-in name dialog functions
\r
6759 \*---------------------------------------------------------------------------*/
\r
6762 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6764 char move[MSG_SIZ];
\r
6767 switch (message) {
\r
6768 case WM_INITDIALOG:
\r
6769 move[0] = (char) lParam;
\r
6770 move[1] = NULLCHAR;
\r
6771 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6772 Translate(hDlg, DLG_TypeInName);
\r
6773 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6774 SetWindowText(hInput, move);
\r
6776 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6780 switch (LOWORD(wParam)) {
\r
6782 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6783 appData.userName = strdup(move);
\r
6784 SetUserLogo(); DisplayLogos();
\r
6786 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6787 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6788 DisplayTitle(move);
\r
6792 EndDialog(hDlg, TRUE);
\r
6795 EndDialog(hDlg, FALSE);
\r
6806 PopUpNameDialog(char firstchar)
\r
6810 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6811 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6812 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6813 FreeProcInstance(lpProc);
\r
6816 /*---------------------------------------------------------------------------*\
\r
6820 \*---------------------------------------------------------------------------*/
\r
6822 /* Nonmodal error box */
\r
6823 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6824 WPARAM wParam, LPARAM lParam);
\r
6827 ErrorPopUp(char *title, char *content)
\r
6831 BOOLEAN modal = hwndMain == NULL;
\r
6849 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6850 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6853 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6855 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6856 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6857 hwndMain, (DLGPROC)lpProc);
\r
6858 FreeProcInstance(lpProc);
\r
6865 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6866 if (errorDialog == NULL) return;
\r
6867 DestroyWindow(errorDialog);
\r
6868 errorDialog = NULL;
\r
6869 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6873 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6877 switch (message) {
\r
6878 case WM_INITDIALOG:
\r
6879 GetWindowRect(hDlg, &rChild);
\r
6882 SetWindowPos(hDlg, NULL, rChild.left,
\r
6883 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6884 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6888 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6889 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6890 and it doesn't work when you resize the dialog.
\r
6891 For now, just give it a default position.
\r
6893 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6894 Translate(hDlg, DLG_Error);
\r
6896 errorDialog = hDlg;
\r
6897 SetWindowText(hDlg, errorTitle);
\r
6898 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6902 switch (LOWORD(wParam)) {
\r
6905 if (errorDialog == hDlg) errorDialog = NULL;
\r
6906 DestroyWindow(hDlg);
\r
6918 HWND gothicDialog = NULL;
\r
6921 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6924 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6926 switch (message) {
\r
6927 case WM_INITDIALOG:
\r
6928 GetWindowRect(hDlg, &rChild);
\r
6930 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6934 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6935 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6936 and it doesn't work when you resize the dialog.
\r
6937 For now, just give it a default position.
\r
6939 gothicDialog = hDlg;
\r
6940 SetWindowText(hDlg, errorTitle);
\r
6941 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6945 switch (LOWORD(wParam)) {
\r
6948 if (errorDialog == hDlg) errorDialog = NULL;
\r
6949 DestroyWindow(hDlg);
\r
6961 GothicPopUp(char *title, VariantClass variant)
\r
6964 static char *lastTitle;
\r
6966 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6967 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6969 if(lastTitle != title && gothicDialog != NULL) {
\r
6970 DestroyWindow(gothicDialog);
\r
6971 gothicDialog = NULL;
\r
6973 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6974 title = lastTitle;
\r
6975 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6976 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6977 hwndMain, (DLGPROC)lpProc);
\r
6978 FreeProcInstance(lpProc);
\r
6983 /*---------------------------------------------------------------------------*\
\r
6985 * Ics Interaction console functions
\r
6987 \*---------------------------------------------------------------------------*/
\r
6989 #define HISTORY_SIZE 64
\r
6990 static char *history[HISTORY_SIZE];
\r
6991 int histIn = 0, histP = 0;
\r
6995 SaveInHistory(char *cmd)
\r
6997 if (history[histIn] != NULL) {
\r
6998 free(history[histIn]);
\r
6999 history[histIn] = NULL;
\r
7001 if (*cmd == NULLCHAR) return;
\r
7002 history[histIn] = StrSave(cmd);
\r
7003 histIn = (histIn + 1) % HISTORY_SIZE;
\r
7004 if (history[histIn] != NULL) {
\r
7005 free(history[histIn]);
\r
7007 history[histIn] = NULL;
\r
7013 PrevInHistory(char *cmd)
\r
7016 if (histP == histIn) {
\r
7017 if (history[histIn] != NULL) free(history[histIn]);
\r
7018 history[histIn] = StrSave(cmd);
\r
7020 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
7021 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
7023 return history[histP];
\r
7029 if (histP == histIn) return NULL;
\r
7030 histP = (histP + 1) % HISTORY_SIZE;
\r
7031 return history[histP];
\r
7035 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
7039 hmenu = LoadMenu(hInst, "TextMenu");
\r
7040 h = GetSubMenu(hmenu, 0);
\r
7042 if (strcmp(e->item, "-") == 0) {
\r
7043 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
7044 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
7045 int flags = MF_STRING, j = 0;
\r
7046 if (e->item[0] == '|') {
\r
7047 flags |= MF_MENUBARBREAK;
\r
7050 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
7051 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
7059 WNDPROC consoleTextWindowProc;
\r
7062 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
7064 char buf[MSG_SIZ], name[MSG_SIZ];
\r
7065 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7069 SetWindowText(hInput, command);
\r
7071 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
7073 sel.cpMin = 999999;
\r
7074 sel.cpMax = 999999;
\r
7075 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7080 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7081 if (sel.cpMin == sel.cpMax) {
\r
7082 /* Expand to surrounding word */
\r
7085 tr.chrg.cpMax = sel.cpMin;
\r
7086 tr.chrg.cpMin = --sel.cpMin;
\r
7087 if (sel.cpMin < 0) break;
\r
7088 tr.lpstrText = name;
\r
7089 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
7090 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
7094 tr.chrg.cpMin = sel.cpMax;
\r
7095 tr.chrg.cpMax = ++sel.cpMax;
\r
7096 tr.lpstrText = name;
\r
7097 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
7098 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
7101 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
7102 MessageBeep(MB_ICONEXCLAMATION);
\r
7106 tr.lpstrText = name;
\r
7107 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
7109 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
7110 MessageBeep(MB_ICONEXCLAMATION);
\r
7113 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
7116 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
7117 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
7118 SetWindowText(hInput, buf);
\r
7119 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
7121 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
7122 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
7123 SetWindowText(hInput, buf);
\r
7124 sel.cpMin = 999999;
\r
7125 sel.cpMax = 999999;
\r
7126 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7132 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7137 switch (message) {
\r
7139 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7140 if(wParam=='R') return 0;
\r
7143 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
7146 sel.cpMin = 999999;
\r
7147 sel.cpMax = 999999;
\r
7148 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7149 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
7154 if(wParam != '\022') {
\r
7155 if (wParam == '\t') {
\r
7156 if (GetKeyState(VK_SHIFT) < 0) {
\r
7158 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7159 if (buttonDesc[0].hwnd) {
\r
7160 SetFocus(buttonDesc[0].hwnd);
\r
7162 SetFocus(hwndMain);
\r
7166 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
7169 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7170 JAWS_DELETE( SetFocus(hInput); )
\r
7171 SendMessage(hInput, message, wParam, lParam);
\r
7174 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
7176 case WM_RBUTTONDOWN:
\r
7177 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
7178 /* Move selection here if it was empty */
\r
7180 pt.x = LOWORD(lParam);
\r
7181 pt.y = HIWORD(lParam);
\r
7182 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7183 if (sel.cpMin == sel.cpMax) {
\r
7184 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
7185 sel.cpMax = sel.cpMin;
\r
7186 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7188 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
7189 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
7191 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
7192 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7193 if (sel.cpMin == sel.cpMax) {
\r
7194 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7195 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
7197 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7198 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7200 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
7201 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
7202 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
7203 MenuPopup(hwnd, pt, hmenu, -1);
\r
7207 case WM_RBUTTONUP:
\r
7208 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7209 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7210 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7214 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7216 return SendMessage(hInput, message, wParam, lParam);
\r
7217 case WM_MBUTTONDOWN:
\r
7218 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7220 switch (LOWORD(wParam)) {
\r
7221 case IDM_QuickPaste:
\r
7223 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7224 if (sel.cpMin == sel.cpMax) {
\r
7225 MessageBeep(MB_ICONEXCLAMATION);
\r
7228 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7229 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7230 SendMessage(hInput, WM_PASTE, 0, 0);
\r
7235 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7238 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7241 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7245 int i = LOWORD(wParam) - IDM_CommandX;
\r
7246 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
7247 icsTextMenuEntry[i].command != NULL) {
\r
7248 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
7249 icsTextMenuEntry[i].getname,
\r
7250 icsTextMenuEntry[i].immediate);
\r
7258 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
7261 WNDPROC consoleInputWindowProc;
\r
7264 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7266 char buf[MSG_SIZ];
\r
7268 static BOOL sendNextChar = FALSE;
\r
7269 static BOOL quoteNextChar = FALSE;
\r
7270 InputSource *is = consoleInputSource;
\r
7274 switch (message) {
\r
7276 if (!appData.localLineEditing || sendNextChar) {
\r
7277 is->buf[0] = (CHAR) wParam;
\r
7279 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7280 sendNextChar = FALSE;
\r
7283 if (quoteNextChar) {
\r
7284 buf[0] = (char) wParam;
\r
7285 buf[1] = NULLCHAR;
\r
7286 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7287 quoteNextChar = FALSE;
\r
7291 case '\r': /* Enter key */
\r
7292 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7293 if (consoleEcho) SaveInHistory(is->buf);
\r
7294 is->buf[is->count++] = '\n';
\r
7295 is->buf[is->count] = NULLCHAR;
\r
7296 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7297 if (consoleEcho) {
\r
7298 ConsoleOutput(is->buf, is->count, TRUE);
\r
7299 } else if (appData.localLineEditing) {
\r
7300 ConsoleOutput("\n", 1, TRUE);
\r
7303 case '\033': /* Escape key */
\r
7304 SetWindowText(hwnd, "");
\r
7305 cf.cbSize = sizeof(CHARFORMAT);
\r
7306 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7307 if (consoleEcho) {
\r
7308 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7310 cf.crTextColor = COLOR_ECHOOFF;
\r
7312 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7313 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7315 case '\t': /* Tab key */
\r
7316 if (GetKeyState(VK_SHIFT) < 0) {
\r
7318 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7321 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7322 if (buttonDesc[0].hwnd) {
\r
7323 SetFocus(buttonDesc[0].hwnd);
\r
7325 SetFocus(hwndMain);
\r
7329 case '\023': /* Ctrl+S */
\r
7330 sendNextChar = TRUE;
\r
7332 case '\021': /* Ctrl+Q */
\r
7333 quoteNextChar = TRUE;
\r
7343 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7344 p = PrevInHistory(buf);
\r
7346 SetWindowText(hwnd, p);
\r
7347 sel.cpMin = 999999;
\r
7348 sel.cpMax = 999999;
\r
7349 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7354 p = NextInHistory();
\r
7356 SetWindowText(hwnd, p);
\r
7357 sel.cpMin = 999999;
\r
7358 sel.cpMax = 999999;
\r
7359 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7365 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7369 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7373 case WM_MBUTTONDOWN:
\r
7374 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7375 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7377 case WM_RBUTTONUP:
\r
7378 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7379 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7380 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7384 hmenu = LoadMenu(hInst, "InputMenu");
\r
7385 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7386 if (sel.cpMin == sel.cpMax) {
\r
7387 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7388 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7390 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7391 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7393 pt.x = LOWORD(lParam);
\r
7394 pt.y = HIWORD(lParam);
\r
7395 MenuPopup(hwnd, pt, hmenu, -1);
\r
7399 switch (LOWORD(wParam)) {
\r
7401 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7403 case IDM_SelectAll:
\r
7405 sel.cpMax = -1; /*999999?*/
\r
7406 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7409 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7412 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7415 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7420 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7423 #define CO_MAX 100000
\r
7424 #define CO_TRIM 1000
\r
7427 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7429 static SnapData sd;
\r
7430 HWND hText, hInput;
\r
7432 static int sizeX, sizeY;
\r
7433 int newSizeX, newSizeY;
\r
7437 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7438 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7440 switch (message) {
\r
7442 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7444 ENLINK *pLink = (ENLINK*)lParam;
\r
7445 if (pLink->msg == WM_LBUTTONUP)
\r
7449 tr.chrg = pLink->chrg;
\r
7450 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7451 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7452 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7453 free(tr.lpstrText);
\r
7457 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7458 hwndConsole = hDlg;
\r
7460 consoleTextWindowProc = (WNDPROC)
\r
7461 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7462 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7463 consoleInputWindowProc = (WNDPROC)
\r
7464 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7465 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7466 Colorize(ColorNormal, TRUE);
\r
7467 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7468 ChangedConsoleFont();
\r
7469 GetClientRect(hDlg, &rect);
\r
7470 sizeX = rect.right;
\r
7471 sizeY = rect.bottom;
\r
7472 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7473 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7474 WINDOWPLACEMENT wp;
\r
7475 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7476 wp.length = sizeof(WINDOWPLACEMENT);
\r
7478 wp.showCmd = SW_SHOW;
\r
7479 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7480 wp.rcNormalPosition.left = wpConsole.x;
\r
7481 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7482 wp.rcNormalPosition.top = wpConsole.y;
\r
7483 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7484 SetWindowPlacement(hDlg, &wp);
\r
7487 // [HGM] Chessknight's change 2004-07-13
\r
7488 else { /* Determine Defaults */
\r
7489 WINDOWPLACEMENT wp;
\r
7490 wpConsole.x = wpMain.width + 1;
\r
7491 wpConsole.y = wpMain.y;
\r
7492 wpConsole.width = screenWidth - wpMain.width;
\r
7493 wpConsole.height = wpMain.height;
\r
7494 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7495 wp.length = sizeof(WINDOWPLACEMENT);
\r
7497 wp.showCmd = SW_SHOW;
\r
7498 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7499 wp.rcNormalPosition.left = wpConsole.x;
\r
7500 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7501 wp.rcNormalPosition.top = wpConsole.y;
\r
7502 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7503 SetWindowPlacement(hDlg, &wp);
\r
7506 // Allow hText to highlight URLs and send notifications on them
\r
7507 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7508 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7509 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7510 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7524 if (IsIconic(hDlg)) break;
\r
7525 newSizeX = LOWORD(lParam);
\r
7526 newSizeY = HIWORD(lParam);
\r
7527 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7528 RECT rectText, rectInput;
\r
7530 int newTextHeight, newTextWidth;
\r
7531 GetWindowRect(hText, &rectText);
\r
7532 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7533 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7534 if (newTextHeight < 0) {
\r
7535 newSizeY += -newTextHeight;
\r
7536 newTextHeight = 0;
\r
7538 SetWindowPos(hText, NULL, 0, 0,
\r
7539 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7540 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7541 pt.x = rectInput.left;
\r
7542 pt.y = rectInput.top + newSizeY - sizeY;
\r
7543 ScreenToClient(hDlg, &pt);
\r
7544 SetWindowPos(hInput, NULL,
\r
7545 pt.x, pt.y, /* needs client coords */
\r
7546 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7547 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7553 case WM_GETMINMAXINFO:
\r
7554 /* Prevent resizing window too small */
\r
7555 mmi = (MINMAXINFO *) lParam;
\r
7556 mmi->ptMinTrackSize.x = 100;
\r
7557 mmi->ptMinTrackSize.y = 100;
\r
7560 /* [AS] Snapping */
\r
7561 case WM_ENTERSIZEMOVE:
\r
7562 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7565 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7568 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7570 case WM_EXITSIZEMOVE:
\r
7571 UpdateICSWidth(hText);
\r
7572 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7575 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7583 if (hwndConsole) return;
\r
7584 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7585 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7590 ConsoleOutput(char* data, int length, int forceVisible)
\r
7595 char buf[CO_MAX+1];
\r
7598 static int delayLF = 0;
\r
7599 CHARRANGE savesel, sel;
\r
7601 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7609 while (length--) {
\r
7617 } else if (*p == '\007') {
\r
7618 MyPlaySound(&sounds[(int)SoundBell]);
\r
7625 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7626 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7627 /* Save current selection */
\r
7628 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7629 exlen = GetWindowTextLength(hText);
\r
7630 /* Find out whether current end of text is visible */
\r
7631 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7632 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7633 /* Trim existing text if it's too long */
\r
7634 if (exlen + (q - buf) > CO_MAX) {
\r
7635 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7638 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7639 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7641 savesel.cpMin -= trim;
\r
7642 savesel.cpMax -= trim;
\r
7643 if (exlen < 0) exlen = 0;
\r
7644 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7645 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7647 /* Append the new text */
\r
7648 sel.cpMin = exlen;
\r
7649 sel.cpMax = exlen;
\r
7650 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7651 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7652 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7653 if (forceVisible || exlen == 0 ||
\r
7654 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7655 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7656 /* Scroll to make new end of text visible if old end of text
\r
7657 was visible or new text is an echo of user typein */
\r
7658 sel.cpMin = 9999999;
\r
7659 sel.cpMax = 9999999;
\r
7660 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7661 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7662 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7663 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7665 if (savesel.cpMax == exlen || forceVisible) {
\r
7666 /* Move insert point to new end of text if it was at the old
\r
7667 end of text or if the new text is an echo of user typein */
\r
7668 sel.cpMin = 9999999;
\r
7669 sel.cpMax = 9999999;
\r
7670 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7672 /* Restore previous selection */
\r
7673 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7675 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7682 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7686 COLORREF oldFg, oldBg;
\r
7690 if(copyNumber > 1)
\r
7691 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7693 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7694 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7695 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7698 rect.right = x + squareSize;
\r
7700 rect.bottom = y + squareSize;
\r
7703 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7704 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7705 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7706 &rect, str, strlen(str), NULL);
\r
7708 (void) SetTextColor(hdc, oldFg);
\r
7709 (void) SetBkColor(hdc, oldBg);
\r
7710 (void) SelectObject(hdc, oldFont);
\r
7714 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7715 RECT *rect, char *color, char *flagFell)
\r
7719 COLORREF oldFg, oldBg;
\r
7722 if (twoBoards && partnerUp) return;
\r
7723 if (appData.clockMode) {
\r
7724 if (tinyLayout == 2)
\r
7725 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7727 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7734 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7735 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7737 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7738 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7741 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7745 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7746 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7747 rect, str, strlen(str), NULL);
\r
7748 if(logoHeight > 0 && appData.clockMode) {
\r
7750 str += strlen(color)+2;
\r
7751 r.top = rect->top + logoHeight/2;
\r
7752 r.left = rect->left;
\r
7753 r.right = rect->right;
\r
7754 r.bottom = rect->bottom;
\r
7755 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7756 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7757 &r, str, strlen(str), NULL);
\r
7759 (void) SetTextColor(hdc, oldFg);
\r
7760 (void) SetBkColor(hdc, oldBg);
\r
7761 (void) SelectObject(hdc, oldFont);
\r
7766 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7772 if( count <= 0 ) {
\r
7773 if (appData.debugMode) {
\r
7774 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7777 return ERROR_INVALID_USER_BUFFER;
\r
7780 ResetEvent(ovl->hEvent);
\r
7781 ovl->Offset = ovl->OffsetHigh = 0;
\r
7782 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7786 err = GetLastError();
\r
7787 if (err == ERROR_IO_PENDING) {
\r
7788 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7792 err = GetLastError();
\r
7799 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7804 ResetEvent(ovl->hEvent);
\r
7805 ovl->Offset = ovl->OffsetHigh = 0;
\r
7806 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7810 err = GetLastError();
\r
7811 if (err == ERROR_IO_PENDING) {
\r
7812 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7816 err = GetLastError();
\r
7823 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7824 void CheckForInputBufferFull( InputSource * is )
\r
7826 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7827 /* Look for end of line */
\r
7828 char * p = is->buf;
\r
7830 while( p < is->next && *p != '\n' ) {
\r
7834 if( p >= is->next ) {
\r
7835 if (appData.debugMode) {
\r
7836 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7839 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7840 is->count = (DWORD) -1;
\r
7841 is->next = is->buf;
\r
7847 InputThread(LPVOID arg)
\r
7852 is = (InputSource *) arg;
\r
7853 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7854 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7855 while (is->hThread != NULL) {
\r
7856 is->error = DoReadFile(is->hFile, is->next,
\r
7857 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7858 &is->count, &ovl);
\r
7859 if (is->error == NO_ERROR) {
\r
7860 is->next += is->count;
\r
7862 if (is->error == ERROR_BROKEN_PIPE) {
\r
7863 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7866 is->count = (DWORD) -1;
\r
7867 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7872 CheckForInputBufferFull( is );
\r
7874 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7876 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7878 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7881 CloseHandle(ovl.hEvent);
\r
7882 CloseHandle(is->hFile);
\r
7884 if (appData.debugMode) {
\r
7885 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7892 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7894 NonOvlInputThread(LPVOID arg)
\r
7901 is = (InputSource *) arg;
\r
7902 while (is->hThread != NULL) {
\r
7903 is->error = ReadFile(is->hFile, is->next,
\r
7904 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7905 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7906 if (is->error == NO_ERROR) {
\r
7907 /* Change CRLF to LF */
\r
7908 if (is->next > is->buf) {
\r
7910 i = is->count + 1;
\r
7918 if (prev == '\r' && *p == '\n') {
\r
7930 if (is->error == ERROR_BROKEN_PIPE) {
\r
7931 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7934 is->count = (DWORD) -1;
\r
7938 CheckForInputBufferFull( is );
\r
7940 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7942 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7944 if (is->count < 0) break; /* Quit on error */
\r
7946 CloseHandle(is->hFile);
\r
7951 SocketInputThread(LPVOID arg)
\r
7955 is = (InputSource *) arg;
\r
7956 while (is->hThread != NULL) {
\r
7957 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7958 if ((int)is->count == SOCKET_ERROR) {
\r
7959 is->count = (DWORD) -1;
\r
7960 is->error = WSAGetLastError();
\r
7962 is->error = NO_ERROR;
\r
7963 is->next += is->count;
\r
7964 if (is->count == 0 && is->second == is) {
\r
7965 /* End of file on stderr; quit with no message */
\r
7969 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7971 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7973 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7979 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7983 is = (InputSource *) lParam;
\r
7984 if (is->lineByLine) {
\r
7985 /* Feed in lines one by one */
\r
7986 char *p = is->buf;
\r
7988 while (q < is->next) {
\r
7989 if (*q++ == '\n') {
\r
7990 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7995 /* Move any partial line to the start of the buffer */
\r
7997 while (p < is->next) {
\r
8002 if (is->error != NO_ERROR || is->count == 0) {
\r
8003 /* Notify backend of the error. Note: If there was a partial
\r
8004 line at the end, it is not flushed through. */
\r
8005 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
8008 /* Feed in the whole chunk of input at once */
\r
8009 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
8010 is->next = is->buf;
\r
8014 /*---------------------------------------------------------------------------*\
\r
8016 * Menu enables. Used when setting various modes.
\r
8018 \*---------------------------------------------------------------------------*/
\r
8026 GreyRevert(Boolean grey)
\r
8027 { // [HGM] vari: for retracting variations in local mode
\r
8028 HMENU hmenu = GetMenu(hwndMain);
\r
8029 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
8030 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
8034 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
8036 while (enab->item > 0) {
\r
8037 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
8042 Enables gnuEnables[] = {
\r
8043 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
8044 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
8045 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
8046 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
8047 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
8048 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
8049 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
8050 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
8051 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
8052 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
8053 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
8054 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
8055 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
8058 // Needed to switch from ncp to GNU mode on Engine Load
\r
8059 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
8060 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
8061 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
8062 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
8063 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
8064 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
8065 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
8066 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
8067 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
8068 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
8069 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
8070 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8071 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
8072 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
8076 Enables icsEnables[] = {
\r
8077 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
8078 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
8079 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8080 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8081 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8082 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8083 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
8084 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
8085 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
8086 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
8087 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8088 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
8089 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
8090 { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },
\r
8091 { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },
\r
8092 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
8093 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
8094 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
8095 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
8096 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
8101 Enables zippyEnables[] = {
\r
8102 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8103 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
8104 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
8105 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
8110 Enables ncpEnables[] = {
\r
8111 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
8112 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
8113 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8114 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8115 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8116 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8117 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
8118 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
8119 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
8120 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
8121 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
8122 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
8123 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8124 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
8125 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
8126 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
8127 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
8128 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
8129 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
8130 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
8131 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
8132 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
8136 Enables trainingOnEnables[] = {
\r
8137 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
8138 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
8139 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
8140 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
8141 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
8142 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
8143 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
8144 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8145 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
8149 Enables trainingOffEnables[] = {
\r
8150 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
8151 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
8152 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
8153 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
8154 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
8155 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
8156 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
8157 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8158 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
8162 /* These modify either ncpEnables or gnuEnables */
\r
8163 Enables cmailEnables[] = {
\r
8164 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
8165 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
8166 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
8167 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
8168 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
8169 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
8170 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
8174 Enables machineThinkingEnables[] = {
\r
8175 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8176 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
8177 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
8178 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8179 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
8180 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8181 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8182 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8183 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8184 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
8185 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8186 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8187 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8188 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8189 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
8190 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
8194 Enables userThinkingEnables[] = {
\r
8195 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8196 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
8197 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
8198 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8199 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
8200 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8201 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8202 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8203 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8204 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
8205 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
8206 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
8207 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
8208 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
8209 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
8210 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
8214 /*---------------------------------------------------------------------------*\
\r
8216 * Front-end interface functions exported by XBoard.
\r
8217 * Functions appear in same order as prototypes in frontend.h.
\r
8219 \*---------------------------------------------------------------------------*/
\r
8221 CheckMark(UINT item, int state)
\r
8223 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
8229 static UINT prevChecked = 0;
\r
8230 static int prevPausing = 0;
\r
8233 if (pausing != prevPausing) {
\r
8234 prevPausing = pausing;
\r
8235 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
8236 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
8237 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
8240 switch (gameMode) {
\r
8241 case BeginningOfGame:
\r
8242 if (appData.icsActive)
\r
8243 nowChecked = IDM_IcsClient;
\r
8244 else if (appData.noChessProgram)
\r
8245 nowChecked = IDM_EditGame;
\r
8247 nowChecked = IDM_MachineBlack;
\r
8249 case MachinePlaysBlack:
\r
8250 nowChecked = IDM_MachineBlack;
\r
8252 case MachinePlaysWhite:
\r
8253 nowChecked = IDM_MachineWhite;
\r
8255 case TwoMachinesPlay:
\r
8256 nowChecked = IDM_TwoMachines;
\r
8259 nowChecked = IDM_AnalysisMode;
\r
8262 nowChecked = IDM_AnalyzeFile;
\r
8265 nowChecked = IDM_EditGame;
\r
8267 case PlayFromGameFile:
\r
8268 nowChecked = IDM_LoadGame;
\r
8270 case EditPosition:
\r
8271 nowChecked = IDM_EditPosition;
\r
8274 nowChecked = IDM_Training;
\r
8276 case IcsPlayingWhite:
\r
8277 case IcsPlayingBlack:
\r
8278 case IcsObserving:
\r
8280 nowChecked = IDM_IcsClient;
\r
8287 if(prevChecked == IDM_TwoMachines) // [HGM] 'Machine Match' might have gotten disabled when stopping match
\r
8288 EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_ENABLED);
\r
8289 CheckMark(prevChecked, MF_UNCHECKED);
\r
8290 CheckMark(nowChecked, MF_CHECKED);
\r
8291 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8293 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8294 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8295 MF_BYCOMMAND|MF_ENABLED);
\r
8297 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8298 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8301 prevChecked = nowChecked;
\r
8303 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8304 if (appData.icsActive) {
\r
8305 if (appData.icsEngineAnalyze) {
\r
8306 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8308 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8311 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8317 HMENU hmenu = GetMenu(hwndMain);
\r
8318 SetMenuEnables(hmenu, icsEnables);
\r
8319 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8320 MF_BYCOMMAND|MF_ENABLED);
\r
8322 if (appData.zippyPlay) {
\r
8323 SetMenuEnables(hmenu, zippyEnables);
\r
8324 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8325 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8326 MF_BYCOMMAND|MF_ENABLED);
\r
8334 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8340 HMENU hmenu = GetMenu(hwndMain);
\r
8341 SetMenuEnables(hmenu, ncpEnables);
\r
8342 DrawMenuBar(hwndMain);
\r
8348 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8352 SetTrainingModeOn()
\r
8355 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8356 for (i = 0; i < N_BUTTONS; i++) {
\r
8357 if (buttonDesc[i].hwnd != NULL)
\r
8358 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8363 VOID SetTrainingModeOff()
\r
8366 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8367 for (i = 0; i < N_BUTTONS; i++) {
\r
8368 if (buttonDesc[i].hwnd != NULL)
\r
8369 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8375 SetUserThinkingEnables()
\r
8377 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8381 SetMachineThinkingEnables()
\r
8383 HMENU hMenu = GetMenu(hwndMain);
\r
8384 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8386 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8388 if (gameMode == MachinePlaysBlack) {
\r
8389 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8390 } else if (gameMode == MachinePlaysWhite) {
\r
8391 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8392 } else if (gameMode == TwoMachinesPlay) {
\r
8393 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8399 DisplayTitle(char *str)
\r
8401 char title[MSG_SIZ], *host;
\r
8402 if (str[0] != NULLCHAR) {
\r
8403 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8404 } else if (appData.icsActive) {
\r
8405 if (appData.icsCommPort[0] != NULLCHAR)
\r
8408 host = appData.icsHost;
\r
8409 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8410 } else if (appData.noChessProgram) {
\r
8411 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8413 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8414 strcat(title, ": ");
\r
8415 strcat(title, first.tidy);
\r
8417 SetWindowText(hwndMain, title);
\r
8422 DisplayMessage(char *str1, char *str2)
\r
8426 int remain = MESSAGE_TEXT_MAX - 1;
\r
8429 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8430 messageText[0] = NULLCHAR;
\r
8432 len = strlen(str1);
\r
8433 if (len > remain) len = remain;
\r
8434 strncpy(messageText, str1, len);
\r
8435 messageText[len] = NULLCHAR;
\r
8438 if (*str2 && remain >= 2) {
\r
8440 strcat(messageText, " ");
\r
8443 len = strlen(str2);
\r
8444 if (len > remain) len = remain;
\r
8445 strncat(messageText, str2, len);
\r
8447 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8448 safeStrCpy(lastMsg, messageText, MSG_SIZ);
\r
8450 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8454 hdc = GetDC(hwndMain);
\r
8455 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8456 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8457 &messageRect, messageText, strlen(messageText), NULL);
\r
8458 (void) SelectObject(hdc, oldFont);
\r
8459 (void) ReleaseDC(hwndMain, hdc);
\r
8463 DisplayError(char *str, int error)
\r
8465 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8469 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8471 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8472 NULL, error, LANG_NEUTRAL,
\r
8473 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8475 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8477 ErrorMap *em = errmap;
\r
8478 while (em->err != 0 && em->err != error) em++;
\r
8479 if (em->err != 0) {
\r
8480 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8482 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8487 ErrorPopUp(_("Error"), buf);
\r
8492 DisplayMoveError(char *str)
\r
8494 fromX = fromY = -1;
\r
8495 ClearHighlights();
\r
8496 DrawPosition(FALSE, NULL);
\r
8497 if (appData.popupMoveErrors) {
\r
8498 ErrorPopUp(_("Error"), str);
\r
8500 DisplayMessage(str, "");
\r
8501 moveErrorMessageUp = TRUE;
\r
8506 DisplayFatalError(char *str, int error, int exitStatus)
\r
8508 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8510 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8513 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8514 NULL, error, LANG_NEUTRAL,
\r
8515 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8517 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8519 ErrorMap *em = errmap;
\r
8520 while (em->err != 0 && em->err != error) em++;
\r
8521 if (em->err != 0) {
\r
8522 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8524 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8529 if (appData.debugMode) {
\r
8530 fprintf(debugFP, "%s: %s\n", label, str);
\r
8532 if (appData.popupExitMessage) {
\r
8533 if(appData.icsActive) SendToICS("logout\n"); // [HGM] make sure no new games will be started!
\r
8534 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8535 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8537 ExitEvent(exitStatus);
\r
8542 DisplayInformation(char *str)
\r
8544 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8554 DisplayNote(char *str)
\r
8556 ErrorPopUp(_("Note"), str);
\r
8561 char *title, *question, *replyPrefix;
\r
8566 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8568 static QuestionParams *qp;
\r
8569 char reply[MSG_SIZ];
\r
8572 switch (message) {
\r
8573 case WM_INITDIALOG:
\r
8574 qp = (QuestionParams *) lParam;
\r
8575 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8576 Translate(hDlg, DLG_Question);
\r
8577 SetWindowText(hDlg, qp->title);
\r
8578 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8579 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8583 switch (LOWORD(wParam)) {
\r
8585 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8586 if (*reply) strcat(reply, " ");
\r
8587 len = strlen(reply);
\r
8588 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8589 strcat(reply, "\n");
\r
8590 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8591 EndDialog(hDlg, TRUE);
\r
8592 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8595 EndDialog(hDlg, FALSE);
\r
8606 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8608 QuestionParams qp;
\r
8612 qp.question = question;
\r
8613 qp.replyPrefix = replyPrefix;
\r
8615 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8616 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8617 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8618 FreeProcInstance(lpProc);
\r
8621 /* [AS] Pick FRC position */
\r
8622 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8624 static int * lpIndexFRC;
\r
8630 case WM_INITDIALOG:
\r
8631 lpIndexFRC = (int *) lParam;
\r
8633 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8634 Translate(hDlg, DLG_NewGameFRC);
\r
8636 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8637 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8638 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8639 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8644 switch( LOWORD(wParam) ) {
\r
8646 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8647 EndDialog( hDlg, 0 );
\r
8648 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8651 EndDialog( hDlg, 1 );
\r
8653 case IDC_NFG_Edit:
\r
8654 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8655 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8657 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8660 case IDC_NFG_Random:
\r
8661 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8662 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8675 int index = appData.defaultFrcPosition;
\r
8676 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8678 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8680 if( result == 0 ) {
\r
8681 appData.defaultFrcPosition = index;
\r
8687 /* [AS] Game list options. Refactored by HGM */
\r
8689 HWND gameListOptionsDialog;
\r
8691 // low-level front-end: clear text edit / list widget
\r
8696 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8699 // low-level front-end: clear text edit / list widget
\r
8701 GLT_DeSelectList()
\r
8703 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8706 // low-level front-end: append line to text edit / list widget
\r
8708 GLT_AddToList( char *name )
\r
8711 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8715 // low-level front-end: get line from text edit / list widget
\r
8717 GLT_GetFromList( int index, char *name )
\r
8720 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8726 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8728 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8729 int idx2 = idx1 + delta;
\r
8730 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8732 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8735 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8736 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8737 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8738 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8742 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8746 case WM_INITDIALOG:
\r
8747 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8749 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8750 Translate(hDlg, DLG_GameListOptions);
\r
8752 /* Initialize list */
\r
8753 GLT_TagsToList( lpUserGLT );
\r
8755 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8760 switch( LOWORD(wParam) ) {
\r
8763 EndDialog( hDlg, 0 );
\r
8766 EndDialog( hDlg, 1 );
\r
8769 case IDC_GLT_Default:
\r
8770 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8773 case IDC_GLT_Restore:
\r
8774 GLT_TagsToList( appData.gameListTags );
\r
8778 GLT_MoveSelection( hDlg, -1 );
\r
8781 case IDC_GLT_Down:
\r
8782 GLT_MoveSelection( hDlg, +1 );
\r
8792 int GameListOptions()
\r
8795 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8797 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8799 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8801 if( result == 0 ) {
\r
8802 char *oldTags = appData.gameListTags;
\r
8803 /* [AS] Memory leak here! */
\r
8804 appData.gameListTags = strdup( lpUserGLT );
\r
8805 if(strcmp(oldTags, appData.gameListTags)) // [HGM] redo Game List when we changed something
\r
8806 GameListToListBox(NULL, TRUE, ".", NULL, FALSE, FALSE); // "." as filter is kludge to select all
\r
8813 DisplayIcsInteractionTitle(char *str)
\r
8815 char consoleTitle[MSG_SIZ];
\r
8817 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8818 SetWindowText(hwndConsole, consoleTitle);
\r
8820 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
8821 char buf[MSG_SIZ], *p = buf, *q;
\r
8822 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
8824 q = strchr(p, ';');
\r
8826 if(*p) ChatPopUp(p);
\r
8830 SetActiveWindow(hwndMain);
\r
8834 DrawPosition(int fullRedraw, Board board)
\r
8836 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8839 void NotifyFrontendLogin()
\r
8842 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8848 fromX = fromY = -1;
\r
8849 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8850 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8851 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8852 dragInfo.lastpos = dragInfo.pos;
\r
8853 dragInfo.start.x = dragInfo.start.y = -1;
\r
8854 dragInfo.from = dragInfo.start;
\r
8856 DrawPosition(TRUE, NULL);
\r
8863 CommentPopUp(char *title, char *str)
\r
8865 HWND hwnd = GetActiveWindow();
\r
8866 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8868 SetActiveWindow(hwnd);
\r
8872 CommentPopDown(void)
\r
8874 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8875 if (commentDialog) {
\r
8876 ShowWindow(commentDialog, SW_HIDE);
\r
8878 commentUp = FALSE;
\r
8882 EditCommentPopUp(int index, char *title, char *str)
\r
8884 EitherCommentPopUp(index, title, str, TRUE);
\r
8891 MyPlaySound(&sounds[(int)SoundRoar]);
\r
8898 MyPlaySound(&sounds[(int)SoundMove]);
\r
8901 VOID PlayIcsWinSound()
\r
8903 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8906 VOID PlayIcsLossSound()
\r
8908 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8911 VOID PlayIcsDrawSound()
\r
8913 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8916 VOID PlayIcsUnfinishedSound()
\r
8918 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8924 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8930 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8938 consoleEcho = TRUE;
\r
8939 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8940 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8941 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8950 consoleEcho = FALSE;
\r
8951 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8952 /* This works OK: set text and background both to the same color */
\r
8954 cf.crTextColor = COLOR_ECHOOFF;
\r
8955 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8956 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8959 /* No Raw()...? */
\r
8961 void Colorize(ColorClass cc, int continuation)
\r
8963 currentColorClass = cc;
\r
8964 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8965 consoleCF.crTextColor = textAttribs[cc].color;
\r
8966 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8967 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8973 static char buf[MSG_SIZ];
\r
8974 DWORD bufsiz = MSG_SIZ;
\r
8976 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8977 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8979 if (!GetUserName(buf, &bufsiz)) {
\r
8980 /*DisplayError("Error getting user name", GetLastError());*/
\r
8981 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8989 static char buf[MSG_SIZ];
\r
8990 DWORD bufsiz = MSG_SIZ;
\r
8992 if (!GetComputerName(buf, &bufsiz)) {
\r
8993 /*DisplayError("Error getting host name", GetLastError());*/
\r
8994 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
9001 ClockTimerRunning()
\r
9003 return clockTimerEvent != 0;
\r
9009 if (clockTimerEvent == 0) return FALSE;
\r
9010 KillTimer(hwndMain, clockTimerEvent);
\r
9011 clockTimerEvent = 0;
\r
9016 StartClockTimer(long millisec)
\r
9018 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
9019 (UINT) millisec, NULL);
\r
9023 DisplayWhiteClock(long timeRemaining, int highlight)
\r
9026 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
9028 if(appData.noGUI) return;
\r
9029 hdc = GetDC(hwndMain);
\r
9030 if (!IsIconic(hwndMain)) {
\r
9031 DisplayAClock(hdc, timeRemaining, highlight,
\r
9032 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
9034 if (highlight && iconCurrent == iconBlack) {
\r
9035 iconCurrent = iconWhite;
\r
9036 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
9037 if (IsIconic(hwndMain)) {
\r
9038 DrawIcon(hdc, 2, 2, iconCurrent);
\r
9041 (void) ReleaseDC(hwndMain, hdc);
\r
9043 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
9047 DisplayBlackClock(long timeRemaining, int highlight)
\r
9050 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
9053 if(appData.noGUI) return;
\r
9054 hdc = GetDC(hwndMain);
\r
9055 if (!IsIconic(hwndMain)) {
\r
9056 DisplayAClock(hdc, timeRemaining, highlight,
\r
9057 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
9059 if (highlight && iconCurrent == iconWhite) {
\r
9060 iconCurrent = iconBlack;
\r
9061 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
9062 if (IsIconic(hwndMain)) {
\r
9063 DrawIcon(hdc, 2, 2, iconCurrent);
\r
9066 (void) ReleaseDC(hwndMain, hdc);
\r
9068 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
9073 LoadGameTimerRunning()
\r
9075 return loadGameTimerEvent != 0;
\r
9079 StopLoadGameTimer()
\r
9081 if (loadGameTimerEvent == 0) return FALSE;
\r
9082 KillTimer(hwndMain, loadGameTimerEvent);
\r
9083 loadGameTimerEvent = 0;
\r
9088 StartLoadGameTimer(long millisec)
\r
9090 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
9091 (UINT) millisec, NULL);
\r
9099 char fileTitle[MSG_SIZ];
\r
9101 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
9102 f = OpenFileDialog(hwndMain, "a", defName,
\r
9103 appData.oldSaveStyle ? "gam" : "pgn",
\r
9105 _("Save Game to File"), NULL, fileTitle, NULL);
\r
9107 SaveGame(f, 0, "");
\r
9114 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
9116 if (delayedTimerEvent != 0) {
\r
9117 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
9118 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
9120 KillTimer(hwndMain, delayedTimerEvent);
\r
9121 delayedTimerEvent = 0;
\r
9122 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
9123 delayedTimerCallback();
\r
9125 delayedTimerCallback = cb;
\r
9126 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
9127 (UINT) millisec, NULL);
\r
9130 DelayedEventCallback
\r
9133 if (delayedTimerEvent) {
\r
9134 return delayedTimerCallback;
\r
9141 CancelDelayedEvent()
\r
9143 if (delayedTimerEvent) {
\r
9144 KillTimer(hwndMain, delayedTimerEvent);
\r
9145 delayedTimerEvent = 0;
\r
9149 DWORD GetWin32Priority(int nice)
\r
9150 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
9152 REALTIME_PRIORITY_CLASS 0x00000100
\r
9153 HIGH_PRIORITY_CLASS 0x00000080
\r
9154 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
9155 NORMAL_PRIORITY_CLASS 0x00000020
\r
9156 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
9157 IDLE_PRIORITY_CLASS 0x00000040
\r
9159 if (nice < -15) return 0x00000080;
\r
9160 if (nice < 0) return 0x00008000;
\r
9162 if (nice == 0) return 0x00000020;
\r
9163 if (nice < 15) return 0x00004000;
\r
9164 return 0x00000040;
\r
9167 void RunCommand(char *cmdLine)
\r
9169 /* Now create the child process. */
\r
9170 STARTUPINFO siStartInfo;
\r
9171 PROCESS_INFORMATION piProcInfo;
\r
9173 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9174 siStartInfo.lpReserved = NULL;
\r
9175 siStartInfo.lpDesktop = NULL;
\r
9176 siStartInfo.lpTitle = NULL;
\r
9177 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9178 siStartInfo.cbReserved2 = 0;
\r
9179 siStartInfo.lpReserved2 = NULL;
\r
9180 siStartInfo.hStdInput = NULL;
\r
9181 siStartInfo.hStdOutput = NULL;
\r
9182 siStartInfo.hStdError = NULL;
\r
9184 CreateProcess(NULL,
\r
9185 cmdLine, /* command line */
\r
9186 NULL, /* process security attributes */
\r
9187 NULL, /* primary thread security attrs */
\r
9188 TRUE, /* handles are inherited */
\r
9189 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9190 NULL, /* use parent's environment */
\r
9192 &siStartInfo, /* STARTUPINFO pointer */
\r
9193 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9195 CloseHandle(piProcInfo.hThread);
\r
9198 /* Start a child process running the given program.
\r
9199 The process's standard output can be read from "from", and its
\r
9200 standard input can be written to "to".
\r
9201 Exit with fatal error if anything goes wrong.
\r
9202 Returns an opaque pointer that can be used to destroy the process
\r
9206 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
9208 #define BUFSIZE 4096
\r
9210 HANDLE hChildStdinRd, hChildStdinWr,
\r
9211 hChildStdoutRd, hChildStdoutWr;
\r
9212 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
9213 SECURITY_ATTRIBUTES saAttr;
\r
9215 PROCESS_INFORMATION piProcInfo;
\r
9216 STARTUPINFO siStartInfo;
\r
9218 char buf[MSG_SIZ];
\r
9221 if (appData.debugMode) {
\r
9222 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
9227 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
9228 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
9229 saAttr.bInheritHandle = TRUE;
\r
9230 saAttr.lpSecurityDescriptor = NULL;
\r
9233 * The steps for redirecting child's STDOUT:
\r
9234 * 1. Create anonymous pipe to be STDOUT for child.
\r
9235 * 2. Create a noninheritable duplicate of read handle,
\r
9236 * and close the inheritable read handle.
\r
9239 /* Create a pipe for the child's STDOUT. */
\r
9240 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
9241 return GetLastError();
\r
9244 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
9245 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
9246 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
9247 FALSE, /* not inherited */
\r
9248 DUPLICATE_SAME_ACCESS);
\r
9250 return GetLastError();
\r
9252 CloseHandle(hChildStdoutRd);
\r
9255 * The steps for redirecting child's STDIN:
\r
9256 * 1. Create anonymous pipe to be STDIN for child.
\r
9257 * 2. Create a noninheritable duplicate of write handle,
\r
9258 * and close the inheritable write handle.
\r
9261 /* Create a pipe for the child's STDIN. */
\r
9262 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
9263 return GetLastError();
\r
9266 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
9267 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
9268 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
9269 FALSE, /* not inherited */
\r
9270 DUPLICATE_SAME_ACCESS);
\r
9272 return GetLastError();
\r
9274 CloseHandle(hChildStdinWr);
\r
9276 /* Arrange to (1) look in dir for the child .exe file, and
\r
9277 * (2) have dir be the child's working directory. Interpret
\r
9278 * dir relative to the directory WinBoard loaded from. */
\r
9279 GetCurrentDirectory(MSG_SIZ, buf);
\r
9280 SetCurrentDirectory(installDir);
\r
9281 SetCurrentDirectory(dir);
\r
9283 /* Now create the child process. */
\r
9285 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9286 siStartInfo.lpReserved = NULL;
\r
9287 siStartInfo.lpDesktop = NULL;
\r
9288 siStartInfo.lpTitle = NULL;
\r
9289 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9290 siStartInfo.cbReserved2 = 0;
\r
9291 siStartInfo.lpReserved2 = NULL;
\r
9292 siStartInfo.hStdInput = hChildStdinRd;
\r
9293 siStartInfo.hStdOutput = hChildStdoutWr;
\r
9294 siStartInfo.hStdError = hChildStdoutWr;
\r
9296 fSuccess = CreateProcess(NULL,
\r
9297 cmdLine, /* command line */
\r
9298 NULL, /* process security attributes */
\r
9299 NULL, /* primary thread security attrs */
\r
9300 TRUE, /* handles are inherited */
\r
9301 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9302 NULL, /* use parent's environment */
\r
9304 &siStartInfo, /* STARTUPINFO pointer */
\r
9305 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9307 err = GetLastError();
\r
9308 SetCurrentDirectory(buf); /* return to prev directory */
\r
9313 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
9314 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
9315 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
9318 /* Close the handles we don't need in the parent */
\r
9319 CloseHandle(piProcInfo.hThread);
\r
9320 CloseHandle(hChildStdinRd);
\r
9321 CloseHandle(hChildStdoutWr);
\r
9323 /* Prepare return value */
\r
9324 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9325 cp->kind = CPReal;
\r
9326 cp->hProcess = piProcInfo.hProcess;
\r
9327 cp->pid = piProcInfo.dwProcessId;
\r
9328 cp->hFrom = hChildStdoutRdDup;
\r
9329 cp->hTo = hChildStdinWrDup;
\r
9331 *pr = (void *) cp;
\r
9333 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
9334 2000 where engines sometimes don't see the initial command(s)
\r
9335 from WinBoard and hang. I don't understand how that can happen,
\r
9336 but the Sleep is harmless, so I've put it in. Others have also
\r
9337 reported what may be the same problem, so hopefully this will fix
\r
9338 it for them too. */
\r
9346 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
9348 ChildProc *cp; int result;
\r
9350 cp = (ChildProc *) pr;
\r
9351 if (cp == NULL) return;
\r
9353 switch (cp->kind) {
\r
9355 /* TerminateProcess is considered harmful, so... */
\r
9356 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9357 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9358 /* The following doesn't work because the chess program
\r
9359 doesn't "have the same console" as WinBoard. Maybe
\r
9360 we could arrange for this even though neither WinBoard
\r
9361 nor the chess program uses a console for stdio? */
\r
9362 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9364 /* [AS] Special termination modes for misbehaving programs... */
\r
9365 if( signal & 8 ) {
\r
9366 result = TerminateProcess( cp->hProcess, 0 );
\r
9368 if ( appData.debugMode) {
\r
9369 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9372 else if( signal & 4 ) {
\r
9373 DWORD dw = WaitForSingleObject( cp->hProcess, appData.delayAfterQuit*1000 + 50 ); // Wait 3 seconds at most
\r
9375 if( dw != WAIT_OBJECT_0 ) {
\r
9376 result = TerminateProcess( cp->hProcess, 0 );
\r
9378 if ( appData.debugMode) {
\r
9379 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9385 CloseHandle(cp->hProcess);
\r
9389 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9393 closesocket(cp->sock);
\r
9398 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9399 closesocket(cp->sock);
\r
9400 closesocket(cp->sock2);
\r
9408 InterruptChildProcess(ProcRef pr)
\r
9412 cp = (ChildProc *) pr;
\r
9413 if (cp == NULL) return;
\r
9414 switch (cp->kind) {
\r
9416 /* The following doesn't work because the chess program
\r
9417 doesn't "have the same console" as WinBoard. Maybe
\r
9418 we could arrange for this even though neither WinBoard
\r
9419 nor the chess program uses a console for stdio */
\r
9420 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9425 /* Can't interrupt */
\r
9429 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9436 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9438 char cmdLine[MSG_SIZ];
\r
9440 if (port[0] == NULLCHAR) {
\r
9441 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9443 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9445 return StartChildProcess(cmdLine, "", pr);
\r
9449 /* Code to open TCP sockets */
\r
9452 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9458 struct sockaddr_in sa, mysa;
\r
9459 struct hostent FAR *hp;
\r
9460 unsigned short uport;
\r
9461 WORD wVersionRequested;
\r
9464 /* Initialize socket DLL */
\r
9465 wVersionRequested = MAKEWORD(1, 1);
\r
9466 err = WSAStartup(wVersionRequested, &wsaData);
\r
9467 if (err != 0) return err;
\r
9470 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9471 err = WSAGetLastError();
\r
9476 /* Bind local address using (mostly) don't-care values.
\r
9478 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9479 mysa.sin_family = AF_INET;
\r
9480 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9481 uport = (unsigned short) 0;
\r
9482 mysa.sin_port = htons(uport);
\r
9483 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9484 == SOCKET_ERROR) {
\r
9485 err = WSAGetLastError();
\r
9490 /* Resolve remote host name */
\r
9491 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9492 if (!(hp = gethostbyname(host))) {
\r
9493 unsigned int b0, b1, b2, b3;
\r
9495 err = WSAGetLastError();
\r
9497 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9498 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9499 hp->h_addrtype = AF_INET;
\r
9501 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9502 hp->h_addr_list[0] = (char *) malloc(4);
\r
9503 hp->h_addr_list[0][0] = (char) b0;
\r
9504 hp->h_addr_list[0][1] = (char) b1;
\r
9505 hp->h_addr_list[0][2] = (char) b2;
\r
9506 hp->h_addr_list[0][3] = (char) b3;
\r
9512 sa.sin_family = hp->h_addrtype;
\r
9513 uport = (unsigned short) atoi(port);
\r
9514 sa.sin_port = htons(uport);
\r
9515 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9517 /* Make connection */
\r
9518 if (connect(s, (struct sockaddr *) &sa,
\r
9519 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9520 err = WSAGetLastError();
\r
9525 /* Prepare return value */
\r
9526 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9527 cp->kind = CPSock;
\r
9529 *pr = (ProcRef *) cp;
\r
9535 OpenCommPort(char *name, ProcRef *pr)
\r
9540 char fullname[MSG_SIZ];
\r
9542 if (*name != '\\')
\r
9543 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9545 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9547 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9548 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9549 if (h == (HANDLE) -1) {
\r
9550 return GetLastError();
\r
9554 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9556 /* Accumulate characters until a 100ms pause, then parse */
\r
9557 ct.ReadIntervalTimeout = 100;
\r
9558 ct.ReadTotalTimeoutMultiplier = 0;
\r
9559 ct.ReadTotalTimeoutConstant = 0;
\r
9560 ct.WriteTotalTimeoutMultiplier = 0;
\r
9561 ct.WriteTotalTimeoutConstant = 0;
\r
9562 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9564 /* Prepare return value */
\r
9565 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9566 cp->kind = CPComm;
\r
9569 *pr = (ProcRef *) cp;
\r
9575 OpenLoopback(ProcRef *pr)
\r
9577 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9583 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9588 struct sockaddr_in sa, mysa;
\r
9589 struct hostent FAR *hp;
\r
9590 unsigned short uport;
\r
9591 WORD wVersionRequested;
\r
9594 char stderrPortStr[MSG_SIZ];
\r
9596 /* Initialize socket DLL */
\r
9597 wVersionRequested = MAKEWORD(1, 1);
\r
9598 err = WSAStartup(wVersionRequested, &wsaData);
\r
9599 if (err != 0) return err;
\r
9601 /* Resolve remote host name */
\r
9602 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9603 if (!(hp = gethostbyname(host))) {
\r
9604 unsigned int b0, b1, b2, b3;
\r
9606 err = WSAGetLastError();
\r
9608 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9609 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9610 hp->h_addrtype = AF_INET;
\r
9612 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9613 hp->h_addr_list[0] = (char *) malloc(4);
\r
9614 hp->h_addr_list[0][0] = (char) b0;
\r
9615 hp->h_addr_list[0][1] = (char) b1;
\r
9616 hp->h_addr_list[0][2] = (char) b2;
\r
9617 hp->h_addr_list[0][3] = (char) b3;
\r
9623 sa.sin_family = hp->h_addrtype;
\r
9624 uport = (unsigned short) 514;
\r
9625 sa.sin_port = htons(uport);
\r
9626 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9628 /* Bind local socket to unused "privileged" port address
\r
9630 s = INVALID_SOCKET;
\r
9631 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9632 mysa.sin_family = AF_INET;
\r
9633 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9634 for (fromPort = 1023;; fromPort--) {
\r
9635 if (fromPort < 0) {
\r
9637 return WSAEADDRINUSE;
\r
9639 if (s == INVALID_SOCKET) {
\r
9640 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9641 err = WSAGetLastError();
\r
9646 uport = (unsigned short) fromPort;
\r
9647 mysa.sin_port = htons(uport);
\r
9648 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9649 == SOCKET_ERROR) {
\r
9650 err = WSAGetLastError();
\r
9651 if (err == WSAEADDRINUSE) continue;
\r
9655 if (connect(s, (struct sockaddr *) &sa,
\r
9656 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9657 err = WSAGetLastError();
\r
9658 if (err == WSAEADDRINUSE) {
\r
9669 /* Bind stderr local socket to unused "privileged" port address
\r
9671 s2 = INVALID_SOCKET;
\r
9672 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9673 mysa.sin_family = AF_INET;
\r
9674 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9675 for (fromPort = 1023;; fromPort--) {
\r
9676 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9677 if (fromPort < 0) {
\r
9678 (void) closesocket(s);
\r
9680 return WSAEADDRINUSE;
\r
9682 if (s2 == INVALID_SOCKET) {
\r
9683 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9684 err = WSAGetLastError();
\r
9690 uport = (unsigned short) fromPort;
\r
9691 mysa.sin_port = htons(uport);
\r
9692 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9693 == SOCKET_ERROR) {
\r
9694 err = WSAGetLastError();
\r
9695 if (err == WSAEADDRINUSE) continue;
\r
9696 (void) closesocket(s);
\r
9700 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9701 err = WSAGetLastError();
\r
9702 if (err == WSAEADDRINUSE) {
\r
9704 s2 = INVALID_SOCKET;
\r
9707 (void) closesocket(s);
\r
9708 (void) closesocket(s2);
\r
9714 prevStderrPort = fromPort; // remember port used
\r
9715 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9717 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9718 err = WSAGetLastError();
\r
9719 (void) closesocket(s);
\r
9720 (void) closesocket(s2);
\r
9725 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9726 err = WSAGetLastError();
\r
9727 (void) closesocket(s);
\r
9728 (void) closesocket(s2);
\r
9732 if (*user == NULLCHAR) user = UserName();
\r
9733 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9734 err = WSAGetLastError();
\r
9735 (void) closesocket(s);
\r
9736 (void) closesocket(s2);
\r
9740 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9741 err = WSAGetLastError();
\r
9742 (void) closesocket(s);
\r
9743 (void) closesocket(s2);
\r
9748 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9749 err = WSAGetLastError();
\r
9750 (void) closesocket(s);
\r
9751 (void) closesocket(s2);
\r
9755 (void) closesocket(s2); /* Stop listening */
\r
9757 /* Prepare return value */
\r
9758 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9759 cp->kind = CPRcmd;
\r
9762 *pr = (ProcRef *) cp;
\r
9769 AddInputSource(ProcRef pr, int lineByLine,
\r
9770 InputCallback func, VOIDSTAR closure)
\r
9772 InputSource *is, *is2 = NULL;
\r
9773 ChildProc *cp = (ChildProc *) pr;
\r
9775 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9776 is->lineByLine = lineByLine;
\r
9778 is->closure = closure;
\r
9779 is->second = NULL;
\r
9780 is->next = is->buf;
\r
9781 if (pr == NoProc) {
\r
9782 is->kind = CPReal;
\r
9783 consoleInputSource = is;
\r
9785 is->kind = cp->kind;
\r
9787 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9788 we create all threads suspended so that the is->hThread variable can be
\r
9789 safely assigned, then let the threads start with ResumeThread.
\r
9791 switch (cp->kind) {
\r
9793 is->hFile = cp->hFrom;
\r
9794 cp->hFrom = NULL; /* now owned by InputThread */
\r
9796 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9797 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9801 is->hFile = cp->hFrom;
\r
9802 cp->hFrom = NULL; /* now owned by InputThread */
\r
9804 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9805 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9809 is->sock = cp->sock;
\r
9811 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9812 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9816 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9818 is->sock = cp->sock;
\r
9820 is2->sock = cp->sock2;
\r
9821 is2->second = is2;
\r
9823 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9824 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9826 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9827 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9831 if( is->hThread != NULL ) {
\r
9832 ResumeThread( is->hThread );
\r
9835 if( is2 != NULL && is2->hThread != NULL ) {
\r
9836 ResumeThread( is2->hThread );
\r
9840 return (InputSourceRef) is;
\r
9844 RemoveInputSource(InputSourceRef isr)
\r
9848 is = (InputSource *) isr;
\r
9849 is->hThread = NULL; /* tell thread to stop */
\r
9850 CloseHandle(is->hThread);
\r
9851 if (is->second != NULL) {
\r
9852 is->second->hThread = NULL;
\r
9853 CloseHandle(is->second->hThread);
\r
9857 int no_wrap(char *message, int count)
\r
9859 ConsoleOutput(message, count, FALSE);
\r
9864 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9867 int outCount = SOCKET_ERROR;
\r
9868 ChildProc *cp = (ChildProc *) pr;
\r
9869 static OVERLAPPED ovl;
\r
9871 static int line = 0;
\r
9875 if (appData.noJoin || !appData.useInternalWrap)
\r
9876 return no_wrap(message, count);
\r
9879 int width = get_term_width();
\r
9880 int len = wrap(NULL, message, count, width, &line);
\r
9881 char *msg = malloc(len);
\r
9885 return no_wrap(message, count);
\r
9888 dbgchk = wrap(msg, message, count, width, &line);
\r
9889 if (dbgchk != len && appData.debugMode)
\r
9890 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9891 ConsoleOutput(msg, len, FALSE);
\r
9898 if (ovl.hEvent == NULL) {
\r
9899 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9901 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9903 switch (cp->kind) {
\r
9906 outCount = send(cp->sock, message, count, 0);
\r
9907 if (outCount == SOCKET_ERROR) {
\r
9908 *outError = WSAGetLastError();
\r
9910 *outError = NO_ERROR;
\r
9915 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9916 &dOutCount, NULL)) {
\r
9917 *outError = NO_ERROR;
\r
9918 outCount = (int) dOutCount;
\r
9920 *outError = GetLastError();
\r
9925 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9926 &dOutCount, &ovl);
\r
9927 if (*outError == NO_ERROR) {
\r
9928 outCount = (int) dOutCount;
\r
9938 if(n != 0) Sleep(n);
\r
9942 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9945 /* Ignore delay, not implemented for WinBoard */
\r
9946 return OutputToProcess(pr, message, count, outError);
\r
9951 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9952 char *buf, int count, int error)
\r
9954 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9957 /* see wgamelist.c for Game List functions */
\r
9958 /* see wedittags.c for Edit Tags functions */
\r
9965 char buf[MSG_SIZ];
\r
9968 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9969 f = fopen(buf, "r");
\r
9971 ProcessICSInitScript(f);
\r
9981 StartAnalysisClock()
\r
9983 if (analysisTimerEvent) return;
\r
9984 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9985 (UINT) 2000, NULL);
\r
9989 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9991 highlightInfo.sq[0].x = fromX;
\r
9992 highlightInfo.sq[0].y = fromY;
\r
9993 highlightInfo.sq[1].x = toX;
\r
9994 highlightInfo.sq[1].y = toY;
\r
10000 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
10001 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
10005 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
10007 premoveHighlightInfo.sq[0].x = fromX;
\r
10008 premoveHighlightInfo.sq[0].y = fromY;
\r
10009 premoveHighlightInfo.sq[1].x = toX;
\r
10010 premoveHighlightInfo.sq[1].y = toY;
\r
10014 ClearPremoveHighlights()
\r
10016 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
10017 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
10021 ShutDownFrontEnd()
\r
10023 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
10024 DeleteClipboardTempFiles();
\r
10030 if (IsIconic(hwndMain))
\r
10031 ShowWindow(hwndMain, SW_RESTORE);
\r
10033 SetActiveWindow(hwndMain);
\r
10037 * Prototypes for animation support routines
\r
10039 static void ScreenSquare(int column, int row, POINT * pt);
\r
10040 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
10041 POINT frames[], int * nFrames);
\r
10044 #define kFactor 4
\r
10047 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
10048 { // [HGM] atomic: animate blast wave
\r
10051 explodeInfo.fromX = fromX;
\r
10052 explodeInfo.fromY = fromY;
\r
10053 explodeInfo.toX = toX;
\r
10054 explodeInfo.toY = toY;
\r
10055 for(i=1; i<4*kFactor; i++) {
\r
10056 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
10057 DrawPosition(FALSE, board);
\r
10058 Sleep(appData.animSpeed);
\r
10060 explodeInfo.radius = 0;
\r
10061 DrawPosition(TRUE, board);
\r
10065 AnimateMove(board, fromX, fromY, toX, toY)
\r
10072 ChessSquare piece, victim = EmptySquare, victim2 = EmptySquare;
\r
10073 int x = toX, y = toY, x2 = kill2X;
\r
10074 POINT start, finish, mid;
\r
10075 POINT frames[kFactor * 2 + 1];
\r
10078 if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();
\r
10080 if (!appData.animate) return;
\r
10081 if (doingSizing) return;
\r
10082 if (fromY < 0 || fromX < 0) return;
\r
10083 piece = board[fromY][fromX];
\r
10084 if (piece >= EmptySquare) return;
\r
10086 if(x2 >= 0) toX = kill2X, toY = kill2Y, victim = board[killY][killX], victim2 = board[kill2Y][kill2X]; else
\r
10087 if(killX >= 0) toX = killX, toY = killY, victim = board[killY][killX]; // [HGM] lion: first to kill square
\r
10089 animInfo.from.x = fromX;
\r
10090 animInfo.from.y = fromY;
\r
10094 ScreenSquare(fromX, fromY, &start);
\r
10095 ScreenSquare(toX, toY, &finish);
\r
10097 /* All moves except knight jumps move in straight line */
\r
10098 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
10099 mid.x = start.x + (finish.x - start.x) / 2;
\r
10100 mid.y = start.y + (finish.y - start.y) / 2;
\r
10102 /* Knight: make straight movement then diagonal */
\r
10103 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
10104 mid.x = start.x + (finish.x - start.x) / 2;
\r
10108 mid.y = start.y + (finish.y - start.y) / 2;
\r
10112 /* Don't use as many frames for very short moves */
\r
10113 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
10114 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
10116 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
10118 animInfo.to.x = toX;
\r
10119 animInfo.to.y = toY;
\r
10120 animInfo.lastpos = start;
\r
10121 animInfo.piece = piece;
\r
10122 for (n = 0; n < nFrames; n++) {
\r
10123 animInfo.pos = frames[n];
\r
10124 DrawPosition(FALSE, board);
\r
10125 animInfo.lastpos = animInfo.pos;
\r
10126 Sleep(appData.animSpeed);
\r
10128 animInfo.pos = finish;
\r
10129 DrawPosition(FALSE, board);
\r
10131 if(toX == x2 && toY == kill2Y) {
\r
10132 fromX = toX; fromY = toY; toX = killX; toY = killY; x2 = -1;
\r
10133 board[kill2Y][kill2X] = EmptySquare; goto again;
\r
10135 if(toX != x || toY != y) {
\r
10136 fromX = toX; fromY = toY; toX = x; toY = y;
\r
10137 board[killY][killX] = EmptySquare; goto again;
\r
10140 if(victim2 != EmptySquare) board[kill2Y][kill2X] = victim2;
\r
10141 if(victim != EmptySquare) board[killY][killX] = victim;
\r
10143 animInfo.piece = EmptySquare;
\r
10144 Explode(board, fromX, fromY, toX, toY);
\r
10147 /* Convert board position to corner of screen rect and color */
\r
10150 ScreenSquare(column, row, pt)
\r
10151 int column; int row; POINT * pt;
\r
10154 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
10155 pt->y = lineGap + row * (squareSize + lineGap) + border;
\r
10157 pt->x = lineGap + column * (squareSize + lineGap) + border;
\r
10158 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
10162 /* Generate a series of frame coords from start->mid->finish.
\r
10163 The movement rate doubles until the half way point is
\r
10164 reached, then halves back down to the final destination,
\r
10165 which gives a nice slow in/out effect. The algorithmn
\r
10166 may seem to generate too many intermediates for short
\r
10167 moves, but remember that the purpose is to attract the
\r
10168 viewers attention to the piece about to be moved and
\r
10169 then to where it ends up. Too few frames would be less
\r
10173 Tween(start, mid, finish, factor, frames, nFrames)
\r
10174 POINT * start; POINT * mid;
\r
10175 POINT * finish; int factor;
\r
10176 POINT frames[]; int * nFrames;
\r
10178 int n, fraction = 1, count = 0;
\r
10180 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
10181 for (n = 0; n < factor; n++)
\r
10183 for (n = 0; n < factor; n++) {
\r
10184 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
10185 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
10187 fraction = fraction / 2;
\r
10191 frames[count] = *mid;
\r
10194 /* Slow out, stepping 1/2, then 1/4, ... */
\r
10196 for (n = 0; n < factor; n++) {
\r
10197 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
10198 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
10200 fraction = fraction * 2;
\r
10202 *nFrames = count;
\r
10206 SettingsPopUp(ChessProgramState *cps)
\r
10207 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
10208 EngineOptionsPopup(savedHwnd, cps);
\r
10211 int flock(int fid, int code)
\r
10213 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
10215 ov.hEvent = NULL;
\r
10217 ov.OffsetHigh = 0;
\r
10219 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
10221 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
10222 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
10223 default: return -1;
\r
10232 static char col[8][20];
\r
10233 COLORREF color = *(COLORREF *) colorVariable[n];
\r
10235 snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
10240 ActivateTheme (int new)
\r
10241 { // Redo initialization of features depending on options that can occur in themes
\r
10243 if(new) InitDrawingColors();
\r
10244 fontBitmapSquareSize = 0; // request creation of new font pieces
\r
10245 InitDrawingSizes(boardSize, 0);
\r
10246 InvalidateRect(hwndMain, NULL, TRUE);
\r