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
2731 if(gameInfo.variant == VariantShogi && BOARD_HEIGHT != 7) { /* promoted Gold representations (but not in Tori!)*/
\r
2732 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2733 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2734 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2735 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2736 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2737 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2738 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2739 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2740 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2741 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2742 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2743 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2745 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2746 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2747 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2748 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2749 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2750 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2751 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2752 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2753 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2754 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2755 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2756 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2759 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2760 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2761 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2762 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2763 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2764 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2765 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2766 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2767 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2768 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2769 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2770 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2771 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2772 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2773 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2777 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2778 /* special Shogi support in this size */
\r
2779 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2780 for (piece = WhitePawn;
\r
2781 (int) piece < (int) BlackPawn;
\r
2782 piece = (ChessSquare) ((int) piece + 1)) {
\r
2783 if (pieceBitmap[i][piece] != NULL)
\r
2784 DeleteObject(pieceBitmap[i][piece]);
\r
2787 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2788 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2789 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2790 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2791 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2792 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2793 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2794 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2795 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2796 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2797 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2798 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2799 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2800 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2801 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2802 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2803 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2804 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2805 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2806 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2807 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2808 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2809 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2810 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2811 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2812 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2813 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2814 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2815 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2816 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2817 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2818 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2819 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2820 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2821 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2822 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2823 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2824 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2825 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2826 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2827 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2828 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2832 if(appData.pieceDirectory[0]) for(i=WhitePawn; i<BlackPawn; i++) { // try for all missing pieces with new naming convention
\r
2833 char buf[MSG_SIZ];
\r
2834 if(pieceBitmap[0][i]) continue;
\r
2835 snprintf(buf, MSG_SIZ, "piece%d_", i);
\r
2836 pieceBitmap[0][i] = DoLoadBitmap(hInst, buf, squareSize, "s");
\r
2837 pieceBitmap[1][i] = DoLoadBitmap(hInst, buf, squareSize, "o");
\r
2838 pieceBitmap[2][i] = DoLoadBitmap(hInst, buf, squareSize, "w");
\r
2843 PieceBitmap(ChessSquare p, int kind)
\r
2845 if ((int) p >= (int) BlackPawn)
\r
2846 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2848 return pieceBitmap[kind][(int) p];
\r
2851 /***************************************************************/
\r
2853 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2854 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2856 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2857 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2861 SquareToPos(int row, int column, int * x, int * y)
\r
2864 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
2865 *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;
\r
2867 *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;
\r
2868 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
2873 DrawCoordsOnDC(HDC hdc)
\r
2875 static char files[] = "0123456789012345678901221098765432109876543210";
\r
2876 static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";
\r
2877 char str[2] = { NULLCHAR, NULLCHAR };
\r
2878 int oldMode, oldAlign, x, y, start, i;
\r
2882 if (!appData.showCoords)
\r
2885 start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;
\r
2887 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2888 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2889 oldAlign = GetTextAlign(hdc);
\r
2890 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2892 y = boardRect.top + lineGap;
\r
2893 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2896 SetTextAlign(hdc, TA_RIGHT|TA_TOP);
\r
2897 x += border - lineGap - 4; y += squareSize - 6;
\r
2899 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2900 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2901 str[0] = files[start + i];
\r
2902 ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);
\r
2903 y += squareSize + lineGap;
\r
2906 start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;
\r
2909 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2910 x += -border + 4; y += border - squareSize + 6;
\r
2912 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2913 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2914 str[0] = ranks[start + i];
\r
2915 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2916 x += squareSize + lineGap;
\r
2919 SelectObject(hdc, oldBrush);
\r
2920 SetBkMode(hdc, oldMode);
\r
2921 SetTextAlign(hdc, oldAlign);
\r
2922 SelectObject(hdc, oldFont);
\r
2926 DrawGridOnDC(HDC hdc)
\r
2930 if (lineGap != 0) {
\r
2931 oldPen = SelectObject(hdc, gridPen);
\r
2932 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2933 SelectObject(hdc, oldPen);
\r
2937 #define HIGHLIGHT_PEN 0
\r
2938 #define PREMOVE_PEN 1
\r
2941 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2944 HPEN oldPen, hPen;
\r
2945 if (lineGap == 0) return;
\r
2947 x1 = boardRect.left +
\r
2948 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;
\r
2949 y1 = boardRect.top +
\r
2950 lineGap/2 + y * (squareSize + lineGap) + border;
\r
2952 x1 = boardRect.left +
\r
2953 lineGap/2 + x * (squareSize + lineGap) + border;
\r
2954 y1 = boardRect.top +
\r
2955 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;
\r
2957 hPen = pen ? premovePen : highlightPen;
\r
2958 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2959 MoveToEx(hdc, x1, y1, NULL);
\r
2960 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2961 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2962 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2963 LineTo(hdc, x1, y1);
\r
2964 SelectObject(hdc, oldPen);
\r
2968 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2971 for (i=0; i<2; i++) {
\r
2972 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2973 DrawHighlightOnDC(hdc, TRUE,
\r
2974 h->sq[i].x, h->sq[i].y,
\r
2979 /* Note: sqcolor is used only in monoMode */
\r
2980 /* Note that this code is largely duplicated in woptions.c,
\r
2981 function DrawSampleSquare, so that needs to be updated too */
\r
2983 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2985 HBITMAP oldBitmap;
\r
2989 if (appData.blindfold) return;
\r
2991 /* [AS] Use font-based pieces if needed */
\r
2992 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2993 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2994 CreatePiecesFromFont();
\r
2996 if( fontBitmapSquareSize == squareSize ) {
\r
2997 int index = TranslatePieceToFontPiece(piece);
\r
2999 SelectObject( tmphdc, hPieceMask[ index ] );
\r
3001 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
3002 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
3006 squareSize, squareSize,
\r
3011 SelectObject( tmphdc, hPieceFace[ index ] );
\r
3013 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
3014 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
3018 squareSize, squareSize,
\r
3027 if (appData.monoMode) {
\r
3028 SelectObject(tmphdc, PieceBitmap(piece,
\r
3029 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
3030 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
3031 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
3033 HBRUSH xBrush = whitePieceBrush;
\r
3034 tmpSize = squareSize;
\r
3035 if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);
\r
3037 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
3038 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
3039 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
3040 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
3041 x += (squareSize - minorSize)>>1;
\r
3042 y += squareSize - minorSize - 2;
\r
3043 tmpSize = minorSize;
\r
3045 if (color || appData.allWhite ) {
\r
3046 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
3048 oldBrush = SelectObject(hdc, xBrush);
\r
3049 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
3050 if(appData.upsideDown && color==flipView)
\r
3051 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
3053 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
3054 /* Use black for outline of white pieces */
\r
3055 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
3056 if(appData.upsideDown && color==flipView)
\r
3057 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
3059 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
3060 } else if(appData.pieceDirectory[0]) {
\r
3061 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
3062 oldBrush = SelectObject(hdc, xBrush);
\r
3063 if(appData.upsideDown && color==flipView)
\r
3064 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
3066 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
3067 SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
3068 if(appData.upsideDown && color==flipView)
\r
3069 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
3071 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
3073 /* Use square color for details of black pieces */
\r
3074 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
3075 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
3076 if(appData.upsideDown && !flipView)
\r
3077 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
3079 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
3081 SelectObject(hdc, oldBrush);
\r
3082 SelectObject(tmphdc, oldBitmap);
\r
3086 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
3087 int GetBackTextureMode( int algo )
\r
3089 int result = BACK_TEXTURE_MODE_DISABLED;
\r
3093 case BACK_TEXTURE_MODE_PLAIN:
\r
3094 result = 1; /* Always use identity map */
\r
3096 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
3097 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
3105 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
3106 to handle redraws cleanly (as random numbers would always be different).
\r
3108 VOID RebuildTextureSquareInfo()
\r
3118 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
3120 if( liteBackTexture != NULL ) {
\r
3121 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3122 lite_w = bi.bmWidth;
\r
3123 lite_h = bi.bmHeight;
\r
3127 if( darkBackTexture != NULL ) {
\r
3128 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3129 dark_w = bi.bmWidth;
\r
3130 dark_h = bi.bmHeight;
\r
3134 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
3135 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
3136 if( (col + row) & 1 ) {
\r
3138 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
3139 if( lite_w >= squareSize*BOARD_WIDTH )
\r
3140 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
3142 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
3143 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
3144 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
3146 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
3147 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
3152 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
3153 if( dark_w >= squareSize*BOARD_WIDTH )
\r
3154 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
3156 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
3157 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
3158 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
3160 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
3161 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
3168 /* [AS] Arrow highlighting support */
\r
3170 static double A_WIDTH = 5; /* Width of arrow body */
\r
3172 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
3173 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
3175 static double Sqr( double x )
\r
3180 static int Round( double x )
\r
3182 return (int) (x + 0.5);
\r
3185 /* Draw an arrow between two points using current settings */
\r
3186 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
3189 double dx, dy, j, k, x, y;
\r
3191 if( d_x == s_x ) {
\r
3192 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3194 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
3197 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
3198 arrow[1].y = d_y - h;
\r
3200 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3201 arrow[2].y = d_y - h;
\r
3206 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3207 arrow[5].y = d_y - h;
\r
3209 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3210 arrow[4].y = d_y - h;
\r
3212 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3215 else if( d_y == s_y ) {
\r
3216 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3219 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3221 arrow[1].x = d_x - w;
\r
3222 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3224 arrow[2].x = d_x - w;
\r
3225 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3230 arrow[5].x = d_x - w;
\r
3231 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3233 arrow[4].x = d_x - w;
\r
3234 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3237 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3240 /* [AS] Needed a lot of paper for this! :-) */
\r
3241 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3242 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3244 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3246 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3251 arrow[0].x = Round(x - j);
\r
3252 arrow[0].y = Round(y + j*dx);
\r
3254 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3255 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3258 x = (double) d_x - k;
\r
3259 y = (double) d_y - k*dy;
\r
3262 x = (double) d_x + k;
\r
3263 y = (double) d_y + k*dy;
\r
3266 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3268 arrow[6].x = Round(x - j);
\r
3269 arrow[6].y = Round(y + j*dx);
\r
3271 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3272 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3274 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3275 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3280 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3281 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3284 Polygon( hdc, arrow, 7 );
\r
3287 /* [AS] Draw an arrow between two squares */
\r
3288 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3290 int s_x, s_y, d_x, d_y;
\r
3297 if( s_col == d_col && s_row == d_row ) {
\r
3301 /* Get source and destination points */
\r
3302 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3303 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3306 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3308 else if( d_y < s_y ) {
\r
3309 d_y += squareSize / 2 + squareSize / 4;
\r
3312 d_y += squareSize / 2;
\r
3316 d_x += squareSize / 2 - squareSize / 4;
\r
3318 else if( d_x < s_x ) {
\r
3319 d_x += squareSize / 2 + squareSize / 4;
\r
3322 d_x += squareSize / 2;
\r
3325 s_x += squareSize / 2;
\r
3326 s_y += squareSize / 2;
\r
3328 /* Adjust width */
\r
3329 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3332 stLB.lbStyle = BS_SOLID;
\r
3333 stLB.lbColor = appData.highlightArrowColor;
\r
3336 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3337 holdpen = SelectObject( hdc, hpen );
\r
3338 hbrush = CreateBrushIndirect( &stLB );
\r
3339 holdbrush = SelectObject( hdc, hbrush );
\r
3341 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3343 SelectObject( hdc, holdpen );
\r
3344 SelectObject( hdc, holdbrush );
\r
3345 DeleteObject( hpen );
\r
3346 DeleteObject( hbrush );
\r
3349 BOOL HasHighlightInfo()
\r
3351 BOOL result = FALSE;
\r
3353 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3354 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3365 BOOL IsDrawArrowEnabled()
\r
3367 BOOL result = FALSE;
\r
3369 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3376 VOID DrawArrowHighlight( HDC hdc )
\r
3378 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3379 DrawArrowBetweenSquares( hdc,
\r
3380 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3381 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3385 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3387 HRGN result = NULL;
\r
3389 if( HasHighlightInfo() ) {
\r
3390 int x1, y1, x2, y2;
\r
3391 int sx, sy, dx, dy;
\r
3393 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3394 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3396 sx = MIN( x1, x2 );
\r
3397 sy = MIN( y1, y2 );
\r
3398 dx = MAX( x1, x2 ) + squareSize;
\r
3399 dy = MAX( y1, y2 ) + squareSize;
\r
3401 result = CreateRectRgn( sx, sy, dx, dy );
\r
3408 Warning: this function modifies the behavior of several other functions.
\r
3410 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3411 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3412 repaint is scattered all over the place, which is not good for features such as
\r
3413 "arrow highlighting" that require a full repaint of the board.
\r
3415 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3416 user interaction, when speed is not so important) but especially to avoid errors
\r
3417 in the displayed graphics.
\r
3419 In such patched places, I always try refer to this function so there is a single
\r
3420 place to maintain knowledge.
\r
3422 To restore the original behavior, just return FALSE unconditionally.
\r
3424 BOOL IsFullRepaintPreferrable()
\r
3426 BOOL result = FALSE;
\r
3428 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3429 /* Arrow may appear on the board */
\r
3437 This function is called by DrawPosition to know whether a full repaint must
\r
3440 Only DrawPosition may directly call this function, which makes use of
\r
3441 some state information. Other function should call DrawPosition specifying
\r
3442 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3444 BOOL DrawPositionNeedsFullRepaint()
\r
3446 BOOL result = FALSE;
\r
3449 Probably a slightly better policy would be to trigger a full repaint
\r
3450 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3451 but animation is fast enough that it's difficult to notice.
\r
3453 if( animInfo.piece == EmptySquare ) {
\r
3454 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3462 static HBITMAP borderBitmap;
\r
3465 DrawBackgroundOnDC(HDC hdc)
\r
3471 static char oldBorder[MSG_SIZ];
\r
3472 int w = 600, h = 600, mode;
\r
3474 if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid
\r
3475 strncpy(oldBorder, appData.border, MSG_SIZ-1);
\r
3476 borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
3478 if(borderBitmap == NULL) { // loading failed, use white
\r
3479 FillRect( hdc, &boardRect, whitePieceBrush );
\r
3482 tmphdc = CreateCompatibleDC(hdc);
\r
3483 hbm = SelectObject(tmphdc, borderBitmap);
\r
3484 if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {
\r
3488 mode = SetStretchBltMode(hdc, COLORONCOLOR);
\r
3489 StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left,
\r
3490 boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3491 SetStretchBltMode(hdc, mode);
\r
3492 SelectObject(tmphdc, hbm);
\r
3497 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3499 int row, column, x, y, square_color, piece_color;
\r
3500 ChessSquare piece;
\r
3502 HDC texture_hdc = NULL;
\r
3504 /* [AS] Initialize background textures if needed */
\r
3505 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3506 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3507 if( backTextureSquareSize != squareSize
\r
3508 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3509 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3510 backTextureSquareSize = squareSize;
\r
3511 RebuildTextureSquareInfo();
\r
3514 texture_hdc = CreateCompatibleDC( hdc );
\r
3517 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3518 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3520 SquareToPos(row, column, &x, &y);
\r
3522 piece = board[row][column];
\r
3524 square_color = ((column + row) % 2) == 1;
\r
3525 if( gameInfo.variant == VariantXiangqi ) {
\r
3526 square_color = !InPalace(row, column);
\r
3527 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3528 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3530 piece_color = (int) piece < (int) BlackPawn;
\r
3533 /* [HGM] holdings file: light square or black */
\r
3534 if(column == BOARD_LEFT-2) {
\r
3535 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3538 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3542 if(column == BOARD_RGHT + 1 ) {
\r
3543 if( row < gameInfo.holdingsSize )
\r
3546 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3550 if(column == BOARD_LEFT-1 ) /* left align */
\r
3551 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3552 else if( column == BOARD_RGHT) /* right align */
\r
3553 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3554 else if( piece == DarkSquare) DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3556 if (appData.monoMode) {
\r
3557 if (piece == EmptySquare) {
\r
3558 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3559 square_color ? WHITENESS : BLACKNESS);
\r
3561 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3564 else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {
\r
3565 /* [AS] Draw the square using a texture bitmap */
\r
3566 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3567 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3568 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3571 squareSize, squareSize,
\r
3574 backTextureSquareInfo[r][c].mode,
\r
3575 backTextureSquareInfo[r][c].x,
\r
3576 backTextureSquareInfo[r][c].y );
\r
3578 SelectObject( texture_hdc, hbm );
\r
3580 if (piece != EmptySquare) {
\r
3581 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3585 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3587 oldBrush = SelectObject(hdc, brush );
\r
3588 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3589 SelectObject(hdc, oldBrush);
\r
3590 if (piece != EmptySquare)
\r
3591 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3596 if( texture_hdc != NULL ) {
\r
3597 DeleteDC( texture_hdc );
\r
3601 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3602 void fputDW(FILE *f, int x)
\r
3604 fputc(x & 255, f);
\r
3605 fputc(x>>8 & 255, f);
\r
3606 fputc(x>>16 & 255, f);
\r
3607 fputc(x>>24 & 255, f);
\r
3610 #define MAX_CLIPS 200 /* more than enough */
\r
3613 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3615 // HBITMAP bufferBitmap;
\r
3620 int w = 100, h = 50;
\r
3622 if(logo == NULL) {
\r
3623 if(!logoHeight) return;
\r
3624 FillRect( hdc, &logoRect, whitePieceBrush );
\r
3626 // GetClientRect(hwndMain, &Rect);
\r
3627 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3628 // Rect.bottom-Rect.top+1);
\r
3629 tmphdc = CreateCompatibleDC(hdc);
\r
3630 hbm = SelectObject(tmphdc, logo);
\r
3631 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3635 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3636 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3637 SelectObject(tmphdc, hbm);
\r
3645 HDC hdc = GetDC(hwndMain);
\r
3646 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3647 if(appData.autoLogo) {
\r
3649 switch(gameMode) { // pick logos based on game mode
\r
3650 case IcsObserving:
\r
3651 whiteLogo = second.programLogo; // ICS logo
\r
3652 blackLogo = second.programLogo;
\r
3655 case IcsPlayingWhite:
\r
3656 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3657 blackLogo = second.programLogo; // ICS logo
\r
3659 case IcsPlayingBlack:
\r
3660 whiteLogo = second.programLogo; // ICS logo
\r
3661 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3663 case TwoMachinesPlay:
\r
3664 if(first.twoMachinesColor[0] == 'b') {
\r
3665 whiteLogo = second.programLogo;
\r
3666 blackLogo = first.programLogo;
\r
3669 case MachinePlaysWhite:
\r
3670 blackLogo = userLogo;
\r
3672 case MachinePlaysBlack:
\r
3673 whiteLogo = userLogo;
\r
3674 blackLogo = first.programLogo;
\r
3677 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3678 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3679 ReleaseDC(hwndMain, hdc);
\r
3684 UpdateLogos(int display)
\r
3685 { // called after loading new engine(s), in tourney or from menu
\r
3686 LoadLogo(&first, 0, FALSE);
\r
3687 LoadLogo(&second, 1, appData.icsActive);
\r
3688 InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos
\r
3689 if(display) DisplayLogos();
\r
3692 static HDC hdcSeek;
\r
3694 // [HGM] seekgraph
\r
3695 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3698 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3699 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3700 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3701 SelectObject( hdcSeek, hp );
\r
3704 // front-end wrapper for drawing functions to do rectangles
\r
3705 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3710 if (hdcSeek == NULL) {
\r
3711 hdcSeek = GetDC(hwndMain);
\r
3712 if (!appData.monoMode) {
\r
3713 SelectPalette(hdcSeek, hPal, FALSE);
\r
3714 RealizePalette(hdcSeek);
\r
3717 hp = SelectObject( hdcSeek, gridPen );
\r
3718 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3719 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3720 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3721 SelectObject( hdcSeek, hp );
\r
3724 // front-end wrapper for putting text in graph
\r
3725 void DrawSeekText(char *buf, int x, int y)
\r
3728 SetBkMode( hdcSeek, TRANSPARENT );
\r
3729 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3730 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3733 void DrawSeekDot(int x, int y, int color)
\r
3735 int square = color & 0x80;
\r
3736 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3737 color == 0 ? markerBrush[1] : color == 1 ? darkSquareBrush : explodeBrush);
\r
3740 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3741 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3743 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3744 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3745 SelectObject(hdcSeek, oldBrush);
\r
3748 void DrawSeekOpen()
\r
3752 void DrawSeekClose()
\r
3760 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3762 static Board lastReq[2], lastDrawn[2];
\r
3763 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3764 static int lastDrawnFlipView = 0;
\r
3765 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3766 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3769 HBITMAP bufferBitmap;
\r
3770 HBITMAP oldBitmap;
\r
3772 HRGN clips[MAX_CLIPS];
\r
3773 ChessSquare dragged_piece = EmptySquare;
\r
3774 int nr = twoBoards*partnerUp;
\r
3776 /* I'm undecided on this - this function figures out whether a full
\r
3777 * repaint is necessary on its own, so there's no real reason to have the
\r
3778 * caller tell it that. I think this can safely be set to FALSE - but
\r
3779 * if we trust the callers not to request full repaints unnessesarily, then
\r
3780 * we could skip some clipping work. In other words, only request a full
\r
3781 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3782 * gamestart and similar) --Hawk
\r
3784 Boolean fullrepaint = repaint;
\r
3786 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3788 if( DrawPositionNeedsFullRepaint() ) {
\r
3789 fullrepaint = TRUE;
\r
3792 if (board == NULL) {
\r
3793 if (!lastReqValid[nr]) {
\r
3796 board = lastReq[nr];
\r
3798 CopyBoard(lastReq[nr], board);
\r
3799 lastReqValid[nr] = 1;
\r
3802 if (doingSizing) {
\r
3806 if (IsIconic(hwndMain)) {
\r
3810 if (hdc == NULL) {
\r
3811 hdc = GetDC(hwndMain);
\r
3812 if (!appData.monoMode) {
\r
3813 SelectPalette(hdc, hPal, FALSE);
\r
3814 RealizePalette(hdc);
\r
3818 releaseDC = FALSE;
\r
3821 /* Create some work-DCs */
\r
3822 hdcmem = CreateCompatibleDC(hdc);
\r
3823 tmphdc = CreateCompatibleDC(hdc);
\r
3825 /* If dragging is in progress, we temporarely remove the piece */
\r
3826 /* [HGM] or temporarily decrease count if stacked */
\r
3827 /* !! Moved to before board compare !! */
\r
3828 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3829 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3830 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3831 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3832 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3834 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3835 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3836 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3838 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3841 /* Figure out which squares need updating by comparing the
\r
3842 * newest board with the last drawn board and checking if
\r
3843 * flipping has changed.
\r
3845 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3846 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3847 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3848 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3849 SquareToPos(row, column, &x, &y);
\r
3850 clips[num_clips++] =
\r
3851 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3855 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3856 for (i=0; i<2; i++) {
\r
3857 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3858 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3859 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3860 lastDrawnHighlight.sq[i].y >= 0) {
\r
3861 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3862 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3863 clips[num_clips++] =
\r
3864 CreateRectRgn(x - lineGap, y - lineGap,
\r
3865 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3867 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3868 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3869 clips[num_clips++] =
\r
3870 CreateRectRgn(x - lineGap, y - lineGap,
\r
3871 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3875 for (i=0; i<2; i++) {
\r
3876 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3877 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3878 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3879 lastDrawnPremove.sq[i].y >= 0) {
\r
3880 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3881 lastDrawnPremove.sq[i].x, &x, &y);
\r
3882 clips[num_clips++] =
\r
3883 CreateRectRgn(x - lineGap, y - lineGap,
\r
3884 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3886 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3887 premoveHighlightInfo.sq[i].y >= 0) {
\r
3888 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3889 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3890 clips[num_clips++] =
\r
3891 CreateRectRgn(x - lineGap, y - lineGap,
\r
3892 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3896 } else { // nr == 1
\r
3897 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3898 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3899 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3900 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3901 for (i=0; i<2; i++) {
\r
3902 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3903 partnerHighlightInfo.sq[i].y >= 0) {
\r
3904 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3905 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3906 clips[num_clips++] =
\r
3907 CreateRectRgn(x - lineGap, y - lineGap,
\r
3908 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3910 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3911 oldPartnerHighlight.sq[i].y >= 0) {
\r
3912 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3913 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3914 clips[num_clips++] =
\r
3915 CreateRectRgn(x - lineGap, y - lineGap,
\r
3916 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3921 fullrepaint = TRUE;
\r
3924 /* Create a buffer bitmap - this is the actual bitmap
\r
3925 * being written to. When all the work is done, we can
\r
3926 * copy it to the real DC (the screen). This avoids
\r
3927 * the problems with flickering.
\r
3929 GetClientRect(hwndMain, &Rect);
\r
3930 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3931 Rect.bottom-Rect.top+1);
\r
3932 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3933 if (!appData.monoMode) {
\r
3934 SelectPalette(hdcmem, hPal, FALSE);
\r
3937 /* Create clips for dragging */
\r
3938 if (!fullrepaint) {
\r
3939 if (dragInfo.from.x >= 0) {
\r
3940 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3941 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3943 if (dragInfo.start.x >= 0) {
\r
3944 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3945 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3947 if (dragInfo.pos.x >= 0) {
\r
3948 x = dragInfo.pos.x - squareSize / 2;
\r
3949 y = dragInfo.pos.y - squareSize / 2;
\r
3950 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3952 if (dragInfo.lastpos.x >= 0) {
\r
3953 x = dragInfo.lastpos.x - squareSize / 2;
\r
3954 y = dragInfo.lastpos.y - squareSize / 2;
\r
3955 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3959 /* Are we animating a move?
\r
3961 * - remove the piece from the board (temporarely)
\r
3962 * - calculate the clipping region
\r
3964 if (!fullrepaint) {
\r
3965 if (animInfo.piece != EmptySquare) {
\r
3966 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3967 x = boardRect.left + animInfo.lastpos.x;
\r
3968 y = boardRect.top + animInfo.lastpos.y;
\r
3969 x2 = boardRect.left + animInfo.pos.x;
\r
3970 y2 = boardRect.top + animInfo.pos.y;
\r
3971 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3972 /* Slight kludge. The real problem is that after AnimateMove is
\r
3973 done, the position on the screen does not match lastDrawn.
\r
3974 This currently causes trouble only on e.p. captures in
\r
3975 atomic, where the piece moves to an empty square and then
\r
3976 explodes. The old and new positions both had an empty square
\r
3977 at the destination, but animation has drawn a piece there and
\r
3978 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3980 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3984 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3985 if (num_clips == 0)
\r
3986 fullrepaint = TRUE;
\r
3988 /* Set clipping on the memory DC */
\r
3989 if (!fullrepaint) {
\r
3990 SelectClipRgn(hdcmem, clips[0]);
\r
3991 for (x = 1; x < num_clips; x++) {
\r
3992 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3993 abort(); // this should never ever happen!
\r
3997 /* Do all the drawing to the memory DC */
\r
3998 if(explodeInfo.radius) { // [HGM] atomic
\r
4000 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
4001 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
4002 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
4003 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
4004 x += squareSize/2;
\r
4005 y += squareSize/2;
\r
4006 if(!fullrepaint) {
\r
4007 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
4008 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
4010 DrawGridOnDC(hdcmem);
\r
4011 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
4012 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
4013 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
4014 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
4015 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
4016 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
4017 SelectObject(hdcmem, oldBrush);
\r
4019 if(border) DrawBackgroundOnDC(hdcmem);
\r
4020 DrawGridOnDC(hdcmem);
\r
4021 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
4022 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
4023 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
4025 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
4026 oldPartnerHighlight = partnerHighlightInfo;
\r
4028 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
4030 if(nr == 0) // [HGM] dual: markers only on left board
\r
4031 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
4032 for (column = 0; column < BOARD_WIDTH; column++) {
\r
4033 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
4034 HBRUSH oldBrush = SelectObject(hdcmem, markerBrush[marker[row][column]-1]);
\r
4035 SquareToPos(row, column, &x, &y);
\r
4036 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
4037 x + 3*squareSize/4, y + 3*squareSize/4);
\r
4038 SelectObject(hdcmem, oldBrush);
\r
4043 if( appData.highlightMoveWithArrow ) {
\r
4045 DrawArrowHighlight(hdcmem);
\r
4048 DrawCoordsOnDC(hdcmem);
\r
4050 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
4051 /* to make sure lastDrawn contains what is actually drawn */
\r
4053 /* Put the dragged piece back into place and draw it (out of place!) */
\r
4054 if (dragged_piece != EmptySquare) {
\r
4055 /* [HGM] or restack */
\r
4056 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
4057 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
4059 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
4060 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
4062 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
4063 x = dragInfo.pos.x - squareSize / 2;
\r
4064 y = dragInfo.pos.y - squareSize / 2;
\r
4065 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
4066 ((int) dragInfo.piece < (int) BlackPawn),
\r
4067 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
4070 /* Put the animated piece back into place and draw it */
\r
4071 if (animInfo.piece != EmptySquare) {
\r
4072 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
4073 x = boardRect.left + animInfo.pos.x;
\r
4074 y = boardRect.top + animInfo.pos.y;
\r
4075 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
4076 ((int) animInfo.piece < (int) BlackPawn),
\r
4077 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
4080 /* Release the bufferBitmap by selecting in the old bitmap
\r
4081 * and delete the memory DC
\r
4083 SelectObject(hdcmem, oldBitmap);
\r
4086 /* Set clipping on the target DC */
\r
4087 if (!fullrepaint) {
\r
4088 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
4090 GetRgnBox(clips[x], &rect);
\r
4091 DeleteObject(clips[x]);
\r
4092 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
4093 rect.right + wpMain.width/2, rect.bottom);
\r
4095 SelectClipRgn(hdc, clips[0]);
\r
4096 for (x = 1; x < num_clips; x++) {
\r
4097 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
4098 abort(); // this should never ever happen!
\r
4102 /* Copy the new bitmap onto the screen in one go.
\r
4103 * This way we avoid any flickering
\r
4105 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
4106 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
4107 boardRect.right - boardRect.left,
\r
4108 boardRect.bottom - boardRect.top,
\r
4109 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
4110 if(saveDiagFlag) {
\r
4111 BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData;
\r
4112 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
4113 HBITMAP src = bufferBitmap, obmp; HDC tmp = CreateCompatibleDC(hdc);
\r
4115 bufferBitmap = CreateCompatibleBitmap(hdc, boardRect.right-boardRect.left, Rect.bottom-Rect.top-2*OUTER_MARGIN);
\r
4116 obmp = SelectObject(tmp, bufferBitmap);
\r
4117 BitBlt(tmp, 0, 0, boardRect.right - boardRect.left, Rect.bottom - Rect.top - 2*OUTER_MARGIN,
\r
4118 tmphdc, boardRect.left, OUTER_MARGIN, SRCCOPY);
\r
4119 GetObject(bufferBitmap, sizeof(b), &b);
\r
4120 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\r
4121 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
4122 bih.biWidth = b.bmWidth;
\r
4123 bih.biHeight = b.bmHeight;
\r
4125 bih.biBitCount = b.bmBitsPixel;
\r
4126 bih.biCompression = 0;
\r
4127 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
4128 bih.biXPelsPerMeter = 0;
\r
4129 bih.biYPelsPerMeter = 0;
\r
4130 bih.biClrUsed = 0;
\r
4131 bih.biClrImportant = 0;
\r
4132 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
4133 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
4134 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
4135 // fprintf(diagFile, "%8x\n", (int) pData);
\r
4137 wb = b.bmWidthBytes;
\r
4139 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
4140 int k = ((int*) pData)[i];
\r
4141 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4142 if(j >= 16) break;
\r
4144 if(j >= nrColors) nrColors = j+1;
\r
4146 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
4148 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
4149 for(w=0; w<(wb>>2); w+=2) {
\r
4150 int k = ((int*) pData)[(wb*i>>2) + w];
\r
4151 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4152 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
4153 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
4154 pData[p++] = m | j<<4;
\r
4156 while(p&3) pData[p++] = 0;
\r
4159 wb = ((wb+31)>>5)<<2;
\r
4161 // write BITMAPFILEHEADER
\r
4162 fprintf(diagFile, "BM");
\r
4163 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
4164 fputDW(diagFile, 0);
\r
4165 fputDW(diagFile, 0x36 + (fac?64:0));
\r
4166 // write BITMAPINFOHEADER
\r
4167 fputDW(diagFile, 40);
\r
4168 fputDW(diagFile, b.bmWidth);
\r
4169 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
4170 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
4171 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
4172 fputDW(diagFile, 0);
\r
4173 fputDW(diagFile, 0);
\r
4174 fputDW(diagFile, 0);
\r
4175 fputDW(diagFile, 0);
\r
4176 fputDW(diagFile, 0);
\r
4177 fputDW(diagFile, 0);
\r
4178 // write color table
\r
4180 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
4181 // write bitmap data
\r
4182 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
4183 fputc(pData[i], diagFile);
\r
4186 DeleteObject(bufferBitmap); bufferBitmap = src;
\r
4187 SelectObject(tmp, obmp);
\r
4191 SelectObject(tmphdc, oldBitmap);
\r
4193 /* Massive cleanup */
\r
4194 for (x = 0; x < num_clips; x++)
\r
4195 DeleteObject(clips[x]);
\r
4198 DeleteObject(bufferBitmap);
\r
4201 ReleaseDC(hwndMain, hdc);
\r
4203 if (lastDrawnFlipView != flipView && nr == 0) {
\r
4205 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
4207 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
4210 /* CopyBoard(lastDrawn, board);*/
\r
4211 lastDrawnHighlight = highlightInfo;
\r
4212 lastDrawnPremove = premoveHighlightInfo;
\r
4213 lastDrawnFlipView = flipView;
\r
4214 lastDrawnValid[nr] = 1;
\r
4217 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
4222 saveDiagFlag = 1; diagFile = f;
\r
4223 HDCDrawPosition(NULL, TRUE, NULL);
\r
4231 /*---------------------------------------------------------------------------*\
\r
4232 | CLIENT PAINT PROCEDURE
\r
4233 | This is the main event-handler for the WM_PAINT message.
\r
4235 \*---------------------------------------------------------------------------*/
\r
4237 PaintProc(HWND hwnd)
\r
4243 if((hdc = BeginPaint(hwnd, &ps))) {
\r
4244 if (IsIconic(hwnd)) {
\r
4245 DrawIcon(hdc, 2, 2, iconCurrent);
\r
4247 if (!appData.monoMode) {
\r
4248 SelectPalette(hdc, hPal, FALSE);
\r
4249 RealizePalette(hdc);
\r
4251 HDCDrawPosition(hdc, 1, NULL);
\r
4252 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
4253 flipView = !flipView; partnerUp = !partnerUp;
\r
4254 HDCDrawPosition(hdc, 1, NULL);
\r
4255 flipView = !flipView; partnerUp = !partnerUp;
\r
4258 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
4259 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
4260 ETO_CLIPPED|ETO_OPAQUE,
\r
4261 &messageRect, messageText, strlen(messageText), NULL);
\r
4262 SelectObject(hdc, oldFont);
\r
4263 DisplayBothClocks();
\r
4266 EndPaint(hwnd,&ps);
\r
4274 * If the user selects on a border boundary, return -1; if off the board,
\r
4275 * return -2. Otherwise map the event coordinate to the square.
\r
4276 * The offset boardRect.left or boardRect.top must already have been
\r
4277 * subtracted from x.
\r
4279 int EventToSquare(x, limit)
\r
4284 if (x < lineGap + border)
\r
4286 x -= lineGap + border;
\r
4287 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4289 x /= (squareSize + lineGap);
\r
4301 DropEnable dropEnables[] = {
\r
4302 { 'P', DP_Pawn, N_("Pawn") },
\r
4303 { 'N', DP_Knight, N_("Knight") },
\r
4304 { 'B', DP_Bishop, N_("Bishop") },
\r
4305 { 'R', DP_Rook, N_("Rook") },
\r
4306 { 'Q', DP_Queen, N_("Queen") },
\r
4310 SetupDropMenu(HMENU hmenu)
\r
4312 int i, count, enable;
\r
4314 extern char white_holding[], black_holding[];
\r
4315 char item[MSG_SIZ];
\r
4317 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4318 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4319 dropEnables[i].piece);
\r
4321 while (p && *p++ == dropEnables[i].piece) count++;
\r
4322 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4323 enable = count > 0 || !appData.testLegality
\r
4324 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4325 && !appData.icsActive);
\r
4326 ModifyMenu(hmenu, dropEnables[i].command,
\r
4327 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4328 dropEnables[i].command, item);
\r
4332 void DragPieceBegin(int x, int y, Boolean instantly)
\r
4334 dragInfo.lastpos.x = boardRect.left + x;
\r
4335 dragInfo.lastpos.y = boardRect.top + y;
\r
4336 if(instantly) dragInfo.pos = dragInfo.lastpos;
\r
4337 dragInfo.from.x = fromX;
\r
4338 dragInfo.from.y = fromY;
\r
4339 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4340 dragInfo.start = dragInfo.from;
\r
4341 SetCapture(hwndMain);
\r
4344 void DragPieceEnd(int x, int y)
\r
4347 dragInfo.start.x = dragInfo.start.y = -1;
\r
4348 dragInfo.from = dragInfo.start;
\r
4349 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4352 void ChangeDragPiece(ChessSquare piece)
\r
4354 dragInfo.piece = piece;
\r
4357 /* Event handler for mouse messages */
\r
4359 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4363 static int recursive = 0;
\r
4365 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4368 if (message == WM_MBUTTONUP) {
\r
4369 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4370 to the middle button: we simulate pressing the left button too!
\r
4372 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4373 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4379 pt.x = LOWORD(lParam);
\r
4380 pt.y = HIWORD(lParam);
\r
4381 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4382 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4383 if (!flipView && y >= 0) {
\r
4384 y = BOARD_HEIGHT - 1 - y;
\r
4386 if (flipView && x >= 0) {
\r
4387 x = BOARD_WIDTH - 1 - x;
\r
4390 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4391 controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status
\r
4393 switch (message) {
\r
4394 case WM_LBUTTONDOWN:
\r
4395 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4396 ClockClick(flipClock); break;
\r
4397 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4398 ClockClick(!flipClock); break;
\r
4400 if(dragging) { // [HGM] lion: don't destroy dragging info if we are already dragging
\r
4401 dragInfo.start.x = dragInfo.start.y = -1;
\r
4402 dragInfo.from = dragInfo.start;
\r
4404 if(fromX == -1 && frozen) { // not sure where this is for
\r
4405 fromX = fromY = -1;
\r
4406 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4409 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4410 DrawPosition(TRUE, NULL);
\r
4413 case WM_LBUTTONUP:
\r
4414 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4415 DrawPosition(TRUE, NULL);
\r
4418 case WM_MOUSEMOVE:
\r
4419 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4420 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4421 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4422 if ((appData.animateDragging || appData.highlightDragging)
\r
4423 && (wParam & MK_LBUTTON || dragging == 2)
\r
4424 && dragInfo.from.x >= 0)
\r
4426 BOOL full_repaint = FALSE;
\r
4428 if (appData.animateDragging) {
\r
4429 dragInfo.pos = pt;
\r
4431 if (appData.highlightDragging) {
\r
4432 HoverEvent(highlightInfo.sq[1].x, highlightInfo.sq[1].y, x, y);
\r
4433 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4434 full_repaint = TRUE;
\r
4438 DrawPosition( full_repaint, NULL);
\r
4440 dragInfo.lastpos = dragInfo.pos;
\r
4444 case WM_MOUSEWHEEL: // [DM]
\r
4445 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4446 /* Mouse Wheel is being rolled forward
\r
4447 * Play moves forward
\r
4449 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4450 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4451 /* Mouse Wheel is being rolled backward
\r
4452 * Play moves backward
\r
4454 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4455 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4459 case WM_MBUTTONUP:
\r
4460 case WM_RBUTTONUP:
\r
4462 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4465 case WM_MBUTTONDOWN:
\r
4466 case WM_RBUTTONDOWN:
\r
4469 fromX = fromY = -1;
\r
4470 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4471 dragInfo.start.x = dragInfo.start.y = -1;
\r
4472 dragInfo.from = dragInfo.start;
\r
4473 dragInfo.lastpos = dragInfo.pos;
\r
4474 if (appData.highlightDragging) {
\r
4475 ClearHighlights();
\r
4478 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4479 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4480 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4481 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4482 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4486 DrawPosition(TRUE, NULL);
\r
4488 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4491 if (message == WM_MBUTTONDOWN) {
\r
4492 buttonCount = 3; /* even if system didn't think so */
\r
4493 if (wParam & MK_SHIFT)
\r
4494 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4496 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4497 } else { /* message == WM_RBUTTONDOWN */
\r
4498 /* Just have one menu, on the right button. Windows users don't
\r
4499 think to try the middle one, and sometimes other software steals
\r
4500 it, or it doesn't really exist. */
\r
4501 if(gameInfo.variant != VariantShogi)
\r
4502 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4504 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4508 SetCapture(hwndMain);
\r
4511 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4512 SetupDropMenu(hmenu);
\r
4513 MenuPopup(hwnd, pt, hmenu, -1);
\r
4523 /* Preprocess messages for buttons in main window */
\r
4525 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4527 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4530 for (i=0; i<N_BUTTONS; i++) {
\r
4531 if (buttonDesc[i].id == id) break;
\r
4533 if (i == N_BUTTONS) return 0;
\r
4534 switch (message) {
\r
4539 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4540 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4547 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4550 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4551 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4552 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4553 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4555 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4557 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4558 TypeInEvent((char)wParam);
\r
4564 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4567 static int promoStyle;
\r
4569 /* Process messages for Promotion dialog box */
\r
4571 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4576 switch (message) {
\r
4578 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4579 /* Center the dialog over the application window */
\r
4580 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4581 Translate(hDlg, DLG_PromotionKing);
\r
4582 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4583 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4584 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4585 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4586 SW_SHOW : SW_HIDE);
\r
4587 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4588 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4589 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4590 PieceToChar(WhiteAngel) != '~') ||
\r
4591 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4592 PieceToChar(BlackAngel) != '~') ) ?
\r
4593 SW_SHOW : SW_HIDE);
\r
4594 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4595 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4596 PieceToChar(WhiteMarshall) != '~') ||
\r
4597 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4598 PieceToChar(BlackMarshall) != '~') ) ?
\r
4599 SW_SHOW : SW_HIDE);
\r
4600 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4601 ShowWindow(GetDlgItem(hDlg, PB_Rook), !promoStyle ? SW_SHOW : SW_HIDE);
\r
4602 ShowWindow(GetDlgItem(hDlg, PB_Bishop), !promoStyle ? SW_SHOW : SW_HIDE);
\r
4604 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4605 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4606 SetWindowText(hDlg, "Promote?");
\r
4608 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4609 gameInfo.variant == VariantSuper ?
\r
4610 SW_SHOW : SW_HIDE);
\r
4613 case WM_COMMAND: /* message: received a command */
\r
4614 switch (LOWORD(wParam)) {
\r
4616 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4617 ClearHighlights();
\r
4618 DrawPosition(FALSE, NULL);
\r
4621 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4624 promoChar = promoStyle ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4627 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4628 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4631 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4632 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4634 case PB_Chancellor:
\r
4635 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4637 case PB_Archbishop:
\r
4638 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4641 promoChar = gameInfo.variant == VariantShogi ? '=' : promoStyle ? NULLCHAR :
\r
4642 ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight));
\r
4647 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4648 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4649 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4650 fromX = fromY = -1;
\r
4651 if (!appData.highlightLastMove) {
\r
4652 ClearHighlights();
\r
4653 DrawPosition(FALSE, NULL);
\r
4660 /* Pop up promotion dialog */
\r
4662 PromotionPopup(HWND hwnd)
\r
4666 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4667 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4668 hwnd, (DLGPROC)lpProc);
\r
4669 FreeProcInstance(lpProc);
\r
4673 PromotionPopUp(char choice)
\r
4675 promoStyle = (choice == '+' || IS_SHOGI(gameInfo.variant));
\r
4676 DrawPosition(TRUE, NULL);
\r
4677 PromotionPopup(hwndMain);
\r
4681 LoadGameDialog(HWND hwnd, char* title)
\r
4685 char fileTitle[MSG_SIZ];
\r
4686 f = OpenFileDialog(hwnd, "rb", "",
\r
4687 appData.oldSaveStyle ? "gam" : "pgn",
\r
4689 title, &number, fileTitle, NULL);
\r
4691 cmailMsgLoaded = FALSE;
\r
4692 if (number == 0) {
\r
4693 int error = GameListBuild(f);
\r
4695 DisplayError(_("Cannot build game list"), error);
\r
4696 } else if (!ListEmpty(&gameList) &&
\r
4697 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4698 GameListPopUp(f, fileTitle);
\r
4701 GameListDestroy();
\r
4704 LoadGame(f, number, fileTitle, FALSE);
\r
4708 int get_term_width()
\r
4713 HFONT hfont, hold_font;
\r
4718 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4722 // get the text metrics
\r
4723 hdc = GetDC(hText);
\r
4724 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4725 if (consoleCF.dwEffects & CFE_BOLD)
\r
4726 lf.lfWeight = FW_BOLD;
\r
4727 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4728 lf.lfItalic = TRUE;
\r
4729 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4730 lf.lfStrikeOut = TRUE;
\r
4731 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4732 lf.lfUnderline = TRUE;
\r
4733 hfont = CreateFontIndirect(&lf);
\r
4734 hold_font = SelectObject(hdc, hfont);
\r
4735 GetTextMetrics(hdc, &tm);
\r
4736 SelectObject(hdc, hold_font);
\r
4737 DeleteObject(hfont);
\r
4738 ReleaseDC(hText, hdc);
\r
4740 // get the rectangle
\r
4741 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4743 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4746 void UpdateICSWidth(HWND hText)
\r
4748 LONG old_width, new_width;
\r
4750 new_width = get_term_width(hText, FALSE);
\r
4751 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4752 if (new_width != old_width)
\r
4754 ics_update_width(new_width);
\r
4755 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4760 ChangedConsoleFont()
\r
4763 CHARRANGE tmpsel, sel;
\r
4764 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4765 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4766 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4769 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4770 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4771 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4772 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4773 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4774 * size. This was undocumented in the version of MSVC++ that I had
\r
4775 * when I wrote the code, but is apparently documented now.
\r
4777 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4778 cfmt.bCharSet = f->lf.lfCharSet;
\r
4779 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4780 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4781 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4782 /* Why are the following seemingly needed too? */
\r
4783 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4784 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4785 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4787 tmpsel.cpMax = -1; /*999999?*/
\r
4788 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4789 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4790 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4791 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4793 paraf.cbSize = sizeof(paraf);
\r
4794 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4795 paraf.dxStartIndent = 0;
\r
4796 paraf.dxOffset = WRAP_INDENT;
\r
4797 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4798 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4799 UpdateICSWidth(hText);
\r
4802 /*---------------------------------------------------------------------------*\
\r
4804 * Window Proc for main window
\r
4806 \*---------------------------------------------------------------------------*/
\r
4808 /* Process messages for main window, etc. */
\r
4810 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4817 char fileTitle[MSG_SIZ];
\r
4818 static SnapData sd;
\r
4819 static int peek=0;
\r
4821 switch (message) {
\r
4823 case WM_PAINT: /* message: repaint portion of window */
\r
4827 case WM_ERASEBKGND:
\r
4828 if (IsIconic(hwnd)) {
\r
4829 /* Cheat; change the message */
\r
4830 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4832 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4836 case WM_LBUTTONDOWN:
\r
4837 case WM_MBUTTONDOWN:
\r
4838 case WM_RBUTTONDOWN:
\r
4839 case WM_LBUTTONUP:
\r
4840 case WM_MBUTTONUP:
\r
4841 case WM_RBUTTONUP:
\r
4842 case WM_MOUSEMOVE:
\r
4843 case WM_MOUSEWHEEL:
\r
4844 MouseEvent(hwnd, message, wParam, lParam);
\r
4848 if((char)wParam == '\b') {
\r
4849 ForwardEvent(); peek = 0;
\r
4852 JAWS_KBUP_NAVIGATION
\r
4857 if((char)wParam == '\b') {
\r
4858 if(!peek) BackwardEvent(), peek = 1;
\r
4861 JAWS_KBDOWN_NAVIGATION
\r
4867 JAWS_ALT_INTERCEPT
\r
4869 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4870 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4871 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4872 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4874 SendMessage(h, message, wParam, lParam);
\r
4875 } else if(lParam != KF_REPEAT) {
\r
4876 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4877 TypeInEvent((char)wParam);
\r
4878 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4879 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4884 case WM_PALETTECHANGED:
\r
4885 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4887 HDC hdc = GetDC(hwndMain);
\r
4888 SelectPalette(hdc, hPal, TRUE);
\r
4889 nnew = RealizePalette(hdc);
\r
4891 paletteChanged = TRUE;
\r
4893 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4895 ReleaseDC(hwnd, hdc);
\r
4899 case WM_QUERYNEWPALETTE:
\r
4900 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4902 HDC hdc = GetDC(hwndMain);
\r
4903 paletteChanged = FALSE;
\r
4904 SelectPalette(hdc, hPal, FALSE);
\r
4905 nnew = RealizePalette(hdc);
\r
4907 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4909 ReleaseDC(hwnd, hdc);
\r
4914 case WM_COMMAND: /* message: command from application menu */
\r
4915 wmId = LOWORD(wParam);
\r
4920 SAY("new game enter a move to play against the computer with white");
\r
4923 case IDM_NewGameFRC:
\r
4924 if( NewGameFRC() == 0 ) {
\r
4929 case IDM_NewVariant:
\r
4930 NewVariantPopup(hwnd);
\r
4933 case IDM_LoadGame:
\r
4934 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4937 case IDM_LoadNextGame:
\r
4941 case IDM_LoadPrevGame:
\r
4945 case IDM_ReloadGame:
\r
4949 case IDM_LoadPosition:
\r
4950 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4951 Reset(FALSE, TRUE);
\r
4954 f = OpenFileDialog(hwnd, "rb", "",
\r
4955 appData.oldSaveStyle ? "pos" : "fen",
\r
4957 _("Load Position from File"), &number, fileTitle, NULL);
\r
4959 LoadPosition(f, number, fileTitle);
\r
4963 case IDM_LoadNextPosition:
\r
4964 ReloadPosition(1);
\r
4967 case IDM_LoadPrevPosition:
\r
4968 ReloadPosition(-1);
\r
4971 case IDM_ReloadPosition:
\r
4972 ReloadPosition(0);
\r
4975 case IDM_SaveGame:
\r
4976 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4977 f = OpenFileDialog(hwnd, "a", defName,
\r
4978 appData.oldSaveStyle ? "gam" : "pgn",
\r
4980 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4982 SaveGame(f, 0, "");
\r
4986 case IDM_SavePosition:
\r
4987 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4988 f = OpenFileDialog(hwnd, "a", defName,
\r
4989 appData.oldSaveStyle ? "pos" : "fen",
\r
4991 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4993 SavePosition(f, 0, "");
\r
4997 case IDM_SaveDiagram:
\r
4998 defName = "diagram";
\r
4999 f = OpenFileDialog(hwnd, "wb", defName,
\r
5002 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
5008 case IDM_SaveSelected:
\r
5009 f = OpenFileDialog(hwnd, "a", "",
\r
5012 _("Save Game to File"), NULL, fileTitle, NULL);
\r
5014 SaveSelected(f, 0, "");
\r
5018 case IDM_CreateBook:
\r
5019 CreateBookEvent();
\r
5022 case IDM_CopyGame:
\r
5023 CopyGameToClipboard();
\r
5026 case IDM_PasteGame:
\r
5027 PasteGameFromClipboard();
\r
5030 case IDM_CopyGameListToClipboard:
\r
5031 CopyGameListToClipboard();
\r
5034 /* [AS] Autodetect FEN or PGN data */
\r
5035 case IDM_PasteAny:
\r
5036 PasteGameOrFENFromClipboard();
\r
5039 /* [AS] Move history */
\r
5040 case IDM_ShowMoveHistory:
\r
5041 if( MoveHistoryIsUp() ) {
\r
5042 MoveHistoryPopDown();
\r
5045 MoveHistoryPopUp();
\r
5049 /* [AS] Eval graph */
\r
5050 case IDM_ShowEvalGraph:
\r
5051 if( EvalGraphIsUp() ) {
\r
5052 EvalGraphPopDown();
\r
5056 SetFocus(hwndMain);
\r
5060 /* [AS] Engine output */
\r
5061 case IDM_ShowEngineOutput:
\r
5062 if( EngineOutputIsUp() ) {
\r
5063 EngineOutputPopDown();
\r
5066 EngineOutputPopUp();
\r
5070 /* [AS] User adjudication */
\r
5071 case IDM_UserAdjudication_White:
\r
5072 UserAdjudicationEvent( +1 );
\r
5075 case IDM_UserAdjudication_Black:
\r
5076 UserAdjudicationEvent( -1 );
\r
5079 case IDM_UserAdjudication_Draw:
\r
5080 UserAdjudicationEvent( 0 );
\r
5083 /* [AS] Game list options dialog */
\r
5084 case IDM_GameListOptions:
\r
5085 GameListOptions();
\r
5092 case IDM_CopyPosition:
\r
5093 CopyFENToClipboard();
\r
5096 case IDM_PastePosition:
\r
5097 PasteFENFromClipboard();
\r
5100 case IDM_MailMove:
\r
5104 case IDM_ReloadCMailMsg:
\r
5105 Reset(TRUE, TRUE);
\r
5106 ReloadCmailMsgEvent(FALSE);
\r
5109 case IDM_Minimize:
\r
5110 ShowWindow(hwnd, SW_MINIMIZE);
\r
5117 case IDM_MachineWhite:
\r
5118 MachineWhiteEvent();
\r
5120 * refresh the tags dialog only if it's visible
\r
5122 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
5124 tags = PGNTags(&gameInfo);
\r
5125 TagsPopUp(tags, CmailMsg());
\r
5128 SAY("computer starts playing white");
\r
5131 case IDM_MachineBlack:
\r
5132 MachineBlackEvent();
\r
5134 * refresh the tags dialog only if it's visible
\r
5136 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
5138 tags = PGNTags(&gameInfo);
\r
5139 TagsPopUp(tags, CmailMsg());
\r
5142 SAY("computer starts playing black");
\r
5145 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
5146 if(matchMode) EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_GRAYED);
\r
5147 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
5150 case IDM_TwoMachines:
\r
5151 TwoMachinesEvent();
\r
5154 * refresh the tags dialog only if it's visible
\r
5156 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
5158 tags = PGNTags(&gameInfo);
\r
5159 TagsPopUp(tags, CmailMsg());
\r
5162 SAY("computer starts playing both sides");
\r
5165 case IDM_AnalysisMode:
\r
5166 if(AnalyzeModeEvent()) {
\r
5167 SAY("analyzing current position");
\r
5171 case IDM_AnalyzeFile:
\r
5172 AnalyzeFileEvent();
\r
5175 case IDM_IcsClient:
\r
5179 case IDM_EditGame:
\r
5180 case IDM_EditGame2:
\r
5185 case IDM_EditPosition:
\r
5186 case IDM_EditPosition2:
\r
5187 EditPositionEvent();
\r
5188 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
5191 case IDM_Training:
\r
5195 case IDM_ShowGameList:
\r
5196 ShowGameListProc();
\r
5199 case IDM_EditProgs1:
\r
5200 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
5203 case IDM_LoadProg1:
\r
5204 LoadEnginePopUp(hwndMain, 0);
\r
5207 case IDM_LoadProg2:
\r
5208 LoadEnginePopUp(hwndMain, 1);
\r
5211 case IDM_EditServers:
\r
5212 EditTagsPopUp(icsNames, &icsNames);
\r
5215 case IDM_EditTags:
\r
5220 case IDM_EditBook:
\r
5224 case IDM_EditComment:
\r
5226 if (commentUp && editComment) {
\r
5229 EditCommentEvent();
\r
5250 case IDM_CallFlag:
\r
5270 case IDM_StopObserving:
\r
5271 StopObservingEvent();
\r
5274 case IDM_StopExamining:
\r
5275 StopExaminingEvent();
\r
5279 UploadGameEvent();
\r
5282 case IDM_TypeInMove:
\r
5283 TypeInEvent('\000');
\r
5286 case IDM_TypeInName:
\r
5287 PopUpNameDialog('\000');
\r
5290 case IDM_Backward:
\r
5292 SetFocus(hwndMain);
\r
5299 SetFocus(hwndMain);
\r
5304 SetFocus(hwndMain);
\r
5309 SetFocus(hwndMain);
\r
5312 case OPT_GameListNext: // [HGM] forward these two accelerators to Game List
\r
5313 case OPT_GameListPrev:
\r
5314 if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);
\r
5318 RevertEvent(FALSE);
\r
5321 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5322 RevertEvent(TRUE);
\r
5325 case IDM_TruncateGame:
\r
5326 TruncateGameEvent();
\r
5333 case IDM_RetractMove:
\r
5334 RetractMoveEvent();
\r
5337 case IDM_FlipView:
\r
5338 flipView = !flipView;
\r
5339 DrawPosition(FALSE, NULL);
\r
5342 case IDM_FlipClock:
\r
5343 flipClock = !flipClock;
\r
5344 DisplayBothClocks();
\r
5348 case IDM_MuteSounds:
\r
5349 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5350 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5351 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5354 case IDM_GeneralOptions:
\r
5355 GeneralOptionsPopup(hwnd);
\r
5356 DrawPosition(TRUE, NULL);
\r
5359 case IDM_BoardOptions:
\r
5360 BoardOptionsPopup(hwnd);
\r
5363 case IDM_ThemeOptions:
\r
5364 ThemeOptionsPopup(hwnd);
\r
5367 case IDM_EnginePlayOptions:
\r
5368 EnginePlayOptionsPopup(hwnd);
\r
5371 case IDM_Engine1Options:
\r
5372 EngineOptionsPopup(hwnd, &first);
\r
5375 case IDM_Engine2Options:
\r
5377 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5378 EngineOptionsPopup(hwnd, &second);
\r
5381 case IDM_OptionsUCI:
\r
5382 UciOptionsPopup(hwnd);
\r
5386 TourneyPopup(hwnd);
\r
5389 case IDM_IcsOptions:
\r
5390 IcsOptionsPopup(hwnd);
\r
5394 FontsOptionsPopup(hwnd);
\r
5398 SoundOptionsPopup(hwnd);
\r
5401 case IDM_CommPort:
\r
5402 CommPortOptionsPopup(hwnd);
\r
5405 case IDM_LoadOptions:
\r
5406 LoadOptionsPopup(hwnd);
\r
5409 case IDM_SaveOptions:
\r
5410 SaveOptionsPopup(hwnd);
\r
5413 case IDM_TimeControl:
\r
5414 TimeControlOptionsPopup(hwnd);
\r
5417 case IDM_SaveSettings:
\r
5418 SaveSettings(settingsFileName);
\r
5421 case IDM_SaveSettingsOnExit:
\r
5422 saveSettingsOnExit = !saveSettingsOnExit;
\r
5423 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5424 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5425 MF_CHECKED : MF_UNCHECKED));
\r
5436 case IDM_AboutGame:
\r
5441 appData.debugMode = !appData.debugMode;
\r
5442 if (appData.debugMode) {
\r
5443 char dir[MSG_SIZ];
\r
5444 GetCurrentDirectory(MSG_SIZ, dir);
\r
5445 SetCurrentDirectory(installDir);
\r
5446 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5447 SetCurrentDirectory(dir);
\r
5448 setbuf(debugFP, NULL);
\r
5455 case IDM_HELPCONTENTS:
\r
5456 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5457 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5458 MessageBox (GetFocus(),
\r
5459 _("Unable to activate help"),
\r
5460 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5464 case IDM_HELPSEARCH:
\r
5465 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5466 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5467 MessageBox (GetFocus(),
\r
5468 _("Unable to activate help"),
\r
5469 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5473 case IDM_HELPHELP:
\r
5474 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5475 MessageBox (GetFocus(),
\r
5476 _("Unable to activate help"),
\r
5477 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5482 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5484 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5485 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5486 FreeProcInstance(lpProc);
\r
5489 case IDM_DirectCommand1:
\r
5490 AskQuestionEvent(_("Direct Command"),
\r
5491 _("Send to chess program:"), "", "1");
\r
5493 case IDM_DirectCommand2:
\r
5494 AskQuestionEvent(_("Direct Command"),
\r
5495 _("Send to second chess program:"), "", "2");
\r
5498 case EP_WhitePawn:
\r
5499 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5500 fromX = fromY = -1;
\r
5503 case EP_WhiteKnight:
\r
5504 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5505 fromX = fromY = -1;
\r
5508 case EP_WhiteBishop:
\r
5509 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5510 fromX = fromY = -1;
\r
5513 case EP_WhiteRook:
\r
5514 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5515 fromX = fromY = -1;
\r
5518 case EP_WhiteQueen:
\r
5519 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5520 fromX = fromY = -1;
\r
5523 case EP_WhiteFerz:
\r
5524 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5525 fromX = fromY = -1;
\r
5528 case EP_WhiteWazir:
\r
5529 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5530 fromX = fromY = -1;
\r
5533 case EP_WhiteAlfil:
\r
5534 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5535 fromX = fromY = -1;
\r
5538 case EP_WhiteCannon:
\r
5539 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5540 fromX = fromY = -1;
\r
5543 case EP_WhiteCardinal:
\r
5544 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5545 fromX = fromY = -1;
\r
5548 case EP_WhiteMarshall:
\r
5549 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5550 fromX = fromY = -1;
\r
5553 case EP_WhiteKing:
\r
5554 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5555 fromX = fromY = -1;
\r
5558 case EP_BlackPawn:
\r
5559 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5560 fromX = fromY = -1;
\r
5563 case EP_BlackKnight:
\r
5564 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5565 fromX = fromY = -1;
\r
5568 case EP_BlackBishop:
\r
5569 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5570 fromX = fromY = -1;
\r
5573 case EP_BlackRook:
\r
5574 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5575 fromX = fromY = -1;
\r
5578 case EP_BlackQueen:
\r
5579 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5580 fromX = fromY = -1;
\r
5583 case EP_BlackFerz:
\r
5584 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5585 fromX = fromY = -1;
\r
5588 case EP_BlackWazir:
\r
5589 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5590 fromX = fromY = -1;
\r
5593 case EP_BlackAlfil:
\r
5594 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5595 fromX = fromY = -1;
\r
5598 case EP_BlackCannon:
\r
5599 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5600 fromX = fromY = -1;
\r
5603 case EP_BlackCardinal:
\r
5604 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5605 fromX = fromY = -1;
\r
5608 case EP_BlackMarshall:
\r
5609 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5610 fromX = fromY = -1;
\r
5613 case EP_BlackKing:
\r
5614 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5615 fromX = fromY = -1;
\r
5618 case EP_EmptySquare:
\r
5619 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5620 fromX = fromY = -1;
\r
5623 case EP_ClearBoard:
\r
5624 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5625 fromX = fromY = -1;
\r
5629 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5630 fromX = fromY = -1;
\r
5634 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5635 fromX = fromY = -1;
\r
5639 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5640 fromX = fromY = -1;
\r
5644 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5645 fromX = fromY = -1;
\r
5649 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5650 fromX = fromY = -1;
\r
5654 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5655 fromX = fromY = -1;
\r
5659 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5660 fromX = fromY = -1;
\r
5664 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5665 fromX = fromY = -1;
\r
5669 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5670 fromX = fromY = -1;
\r
5674 barbaric = 0; appData.language = "";
\r
5675 TranslateMenus(0);
\r
5676 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5677 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5678 lastChecked = wmId;
\r
5682 if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)
\r
5683 RecentEngineEvent(wmId - IDM_RecentEngines);
\r
5685 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5686 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5687 TranslateMenus(0);
\r
5688 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5689 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5690 lastChecked = wmId;
\r
5693 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5699 case CLOCK_TIMER_ID:
\r
5700 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5701 clockTimerEvent = 0;
\r
5702 DecrementClocks(); /* call into back end */
\r
5704 case LOAD_GAME_TIMER_ID:
\r
5705 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5706 loadGameTimerEvent = 0;
\r
5707 AutoPlayGameLoop(); /* call into back end */
\r
5709 case ANALYSIS_TIMER_ID:
\r
5710 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5711 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5712 AnalysisPeriodicEvent(0);
\r
5714 KillTimer(hwnd, analysisTimerEvent);
\r
5715 analysisTimerEvent = 0;
\r
5718 case DELAYED_TIMER_ID:
\r
5719 KillTimer(hwnd, delayedTimerEvent);
\r
5720 delayedTimerEvent = 0;
\r
5721 delayedTimerCallback();
\r
5726 case WM_USER_Input:
\r
5727 InputEvent(hwnd, message, wParam, lParam);
\r
5730 /* [AS] Also move "attached" child windows */
\r
5731 case WM_WINDOWPOSCHANGING:
\r
5733 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5734 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5736 if( ((lpwp->flags & SWP_NOMOVE) == 0) /*&& ((lpwp->flags & SWP_NOSIZE) != 0)*/ ) { // [HGM] in Win8 size always accompanies move?
\r
5737 /* Window is moving */
\r
5740 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5741 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5742 rcMain.right = wpMain.x + wpMain.width;
\r
5743 rcMain.top = wpMain.y;
\r
5744 rcMain.bottom = wpMain.y + wpMain.height;
\r
5746 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5747 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5748 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5749 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5750 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5751 wpMain.x = lpwp->x;
\r
5752 wpMain.y = lpwp->y;
\r
5758 /* [AS] Snapping */
\r
5759 case WM_ENTERSIZEMOVE:
\r
5760 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5761 if (hwnd == hwndMain) {
\r
5762 doingSizing = TRUE;
\r
5765 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5769 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5770 if (hwnd == hwndMain) {
\r
5771 lastSizing = wParam;
\r
5776 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5777 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5779 case WM_EXITSIZEMOVE:
\r
5780 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5781 if (hwnd == hwndMain) {
\r
5783 doingSizing = FALSE;
\r
5784 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5785 GetClientRect(hwnd, &client);
\r
5786 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5788 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5790 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5793 case WM_DESTROY: /* message: window being destroyed */
\r
5794 PostQuitMessage(0);
\r
5798 if (hwnd == hwndMain) {
\r
5803 default: /* Passes it on if unprocessed */
\r
5804 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5811 /*---------------------------------------------------------------------------*\
\r
5813 * Misc utility routines
\r
5815 \*---------------------------------------------------------------------------*/
\r
5818 * Decent random number generator, at least not as bad as Windows
\r
5819 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5821 unsigned int randstate;
\r
5826 randstate = randstate * 1664525 + 1013904223;
\r
5827 return (int) randstate & 0x7fffffff;
\r
5831 mysrandom(unsigned int seed)
\r
5838 * returns TRUE if user selects a different color, FALSE otherwise
\r
5842 ChangeColor(HWND hwnd, COLORREF *which)
\r
5844 static BOOL firstTime = TRUE;
\r
5845 static DWORD customColors[16];
\r
5847 COLORREF newcolor;
\r
5852 /* Make initial colors in use available as custom colors */
\r
5853 /* Should we put the compiled-in defaults here instead? */
\r
5855 customColors[i++] = lightSquareColor & 0xffffff;
\r
5856 customColors[i++] = darkSquareColor & 0xffffff;
\r
5857 customColors[i++] = whitePieceColor & 0xffffff;
\r
5858 customColors[i++] = blackPieceColor & 0xffffff;
\r
5859 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5860 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5862 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5863 customColors[i++] = textAttribs[ccl].color;
\r
5865 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5866 firstTime = FALSE;
\r
5869 cc.lStructSize = sizeof(cc);
\r
5870 cc.hwndOwner = hwnd;
\r
5871 cc.hInstance = NULL;
\r
5872 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5873 cc.lpCustColors = (LPDWORD) customColors;
\r
5874 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5876 if (!ChooseColor(&cc)) return FALSE;
\r
5878 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5879 if (newcolor == *which) return FALSE;
\r
5880 *which = newcolor;
\r
5884 InitDrawingColors();
\r
5885 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5890 MyLoadSound(MySound *ms)
\r
5896 if (ms->data && ms->flag) free(ms->data);
\r
5899 switch (ms->name[0]) {
\r
5905 /* System sound from Control Panel. Don't preload here. */
\r
5909 if (ms->name[1] == NULLCHAR) {
\r
5910 /* "!" alone = silence */
\r
5913 /* Builtin wave resource. Error if not found. */
\r
5914 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5915 if (h == NULL) break;
\r
5916 ms->data = (void *)LoadResource(hInst, h);
\r
5917 ms->flag = 0; // not maloced, so cannot be freed!
\r
5918 if (h == NULL) break;
\r
5923 /* .wav file. Error if not found. */
\r
5924 f = fopen(ms->name, "rb");
\r
5925 if (f == NULL) break;
\r
5926 if (fstat(fileno(f), &st) < 0) break;
\r
5927 ms->data = malloc(st.st_size);
\r
5929 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5935 char buf[MSG_SIZ];
\r
5936 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5937 DisplayError(buf, GetLastError());
\r
5943 MyPlaySound(MySound *ms)
\r
5945 BOOLEAN ok = FALSE;
\r
5947 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5948 switch (ms->name[0]) {
\r
5950 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5955 /* System sound from Control Panel (deprecated feature).
\r
5956 "$" alone or an unset sound name gets default beep (still in use). */
\r
5957 if (ms->name[1]) {
\r
5958 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5960 if (!ok) ok = MessageBeep(MB_OK);
\r
5963 /* Builtin wave resource, or "!" alone for silence */
\r
5964 if (ms->name[1]) {
\r
5965 if (ms->data == NULL) return FALSE;
\r
5966 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5972 /* .wav file. Error if not found. */
\r
5973 if (ms->data == NULL) return FALSE;
\r
5974 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5977 /* Don't print an error: this can happen innocently if the sound driver
\r
5978 is busy; for instance, if another instance of WinBoard is playing
\r
5979 a sound at about the same time. */
\r
5985 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5988 OPENFILENAME *ofn;
\r
5989 static UINT *number; /* gross that this is static */
\r
5991 switch (message) {
\r
5992 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5993 /* Center the dialog over the application window */
\r
5994 ofn = (OPENFILENAME *) lParam;
\r
5995 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5996 number = (UINT *) ofn->lCustData;
\r
5997 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
6001 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6002 Translate(hDlg, 1536);
\r
6003 return FALSE; /* Allow for further processing */
\r
6006 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
6007 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
6009 return FALSE; /* Allow for further processing */
\r
6015 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
6017 static UINT *number;
\r
6018 OPENFILENAME *ofname;
\r
6021 case WM_INITDIALOG:
\r
6022 Translate(hdlg, DLG_IndexNumber);
\r
6023 ofname = (OPENFILENAME *)lParam;
\r
6024 number = (UINT *)(ofname->lCustData);
\r
6027 ofnot = (OFNOTIFY *)lParam;
\r
6028 if (ofnot->hdr.code == CDN_FILEOK) {
\r
6029 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
6038 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
6039 char *nameFilt, char *dlgTitle, UINT *number,
\r
6040 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
6042 OPENFILENAME openFileName;
\r
6043 char buf1[MSG_SIZ];
\r
6046 if (fileName == NULL) fileName = buf1;
\r
6047 if (defName == NULL) {
\r
6048 safeStrCpy(fileName, "*.", 3 );
\r
6049 strcat(fileName, defExt);
\r
6051 safeStrCpy(fileName, defName, MSG_SIZ );
\r
6053 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
6054 if (number) *number = 0;
\r
6056 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
6057 openFileName.hwndOwner = hwnd;
\r
6058 openFileName.hInstance = (HANDLE) hInst;
\r
6059 openFileName.lpstrFilter = nameFilt;
\r
6060 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
6061 openFileName.nMaxCustFilter = 0L;
\r
6062 openFileName.nFilterIndex = 1L;
\r
6063 openFileName.lpstrFile = fileName;
\r
6064 openFileName.nMaxFile = MSG_SIZ;
\r
6065 openFileName.lpstrFileTitle = fileTitle;
\r
6066 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
6067 openFileName.lpstrInitialDir = NULL;
\r
6068 openFileName.lpstrTitle = dlgTitle;
\r
6069 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
6070 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
6071 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
6072 | (oldDialog ? 0 : OFN_EXPLORER);
\r
6073 openFileName.nFileOffset = 0;
\r
6074 openFileName.nFileExtension = 0;
\r
6075 openFileName.lpstrDefExt = defExt;
\r
6076 openFileName.lCustData = (LONG) number;
\r
6077 openFileName.lpfnHook = oldDialog ?
\r
6078 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
6079 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
6081 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
6082 GetOpenFileName(&openFileName)) {
\r
6083 /* open the file */
\r
6084 f = fopen(openFileName.lpstrFile, write);
\r
6086 MessageBox(hwnd, _("File open failed"), NULL,
\r
6087 MB_OK|MB_ICONEXCLAMATION);
\r
6091 int err = CommDlgExtendedError();
\r
6092 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
6101 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
6103 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
6106 * Get the first pop-up menu in the menu template. This is the
\r
6107 * menu that TrackPopupMenu displays.
\r
6109 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
6110 TranslateOneMenu(10, hmenuTrackPopup);
\r
6112 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
6115 * TrackPopup uses screen coordinates, so convert the
\r
6116 * coordinates of the mouse click to screen coordinates.
\r
6118 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
6120 /* Draw and track the floating pop-up menu. */
\r
6121 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
6122 pt.x, pt.y, 0, hwnd, NULL);
\r
6124 /* Destroy the menu.*/
\r
6125 DestroyMenu(hmenu);
\r
6130 int sizeX, sizeY, newSizeX, newSizeY;
\r
6132 } ResizeEditPlusButtonsClosure;
\r
6135 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
6137 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
6141 if (hChild == cl->hText) return TRUE;
\r
6142 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
6143 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
6144 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
6145 ScreenToClient(cl->hDlg, &pt);
\r
6146 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
6147 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
6151 /* Resize a dialog that has a (rich) edit field filling most of
\r
6152 the top, with a row of buttons below */
\r
6154 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
6157 int newTextHeight, newTextWidth;
\r
6158 ResizeEditPlusButtonsClosure cl;
\r
6160 /*if (IsIconic(hDlg)) return;*/
\r
6161 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
6163 cl.hdwp = BeginDeferWindowPos(8);
\r
6165 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
6166 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6167 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6168 if (newTextHeight < 0) {
\r
6169 newSizeY += -newTextHeight;
\r
6170 newTextHeight = 0;
\r
6172 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
6173 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6179 cl.newSizeX = newSizeX;
\r
6180 cl.newSizeY = newSizeY;
\r
6181 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
6183 EndDeferWindowPos(cl.hdwp);
\r
6186 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
6188 RECT rChild, rParent;
\r
6189 int wChild, hChild, wParent, hParent;
\r
6190 int wScreen, hScreen, xNew, yNew;
\r
6193 /* Get the Height and Width of the child window */
\r
6194 GetWindowRect (hwndChild, &rChild);
\r
6195 wChild = rChild.right - rChild.left;
\r
6196 hChild = rChild.bottom - rChild.top;
\r
6198 /* Get the Height and Width of the parent window */
\r
6199 GetWindowRect (hwndParent, &rParent);
\r
6200 wParent = rParent.right - rParent.left;
\r
6201 hParent = rParent.bottom - rParent.top;
\r
6203 /* Get the display limits */
\r
6204 hdc = GetDC (hwndChild);
\r
6205 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
6206 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
6207 ReleaseDC(hwndChild, hdc);
\r
6209 /* Calculate new X position, then adjust for screen */
\r
6210 xNew = rParent.left + ((wParent - wChild) /2);
\r
6213 } else if ((xNew+wChild) > wScreen) {
\r
6214 xNew = wScreen - wChild;
\r
6217 /* Calculate new Y position, then adjust for screen */
\r
6219 yNew = rParent.top + ((hParent - hChild) /2);
\r
6222 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
6227 } else if ((yNew+hChild) > hScreen) {
\r
6228 yNew = hScreen - hChild;
\r
6231 /* Set it, and return */
\r
6232 return SetWindowPos (hwndChild, NULL,
\r
6233 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
6236 /* Center one window over another */
\r
6237 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
6239 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
6242 /*---------------------------------------------------------------------------*\
\r
6244 * Startup Dialog functions
\r
6246 \*---------------------------------------------------------------------------*/
\r
6248 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
6250 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6252 while (*cd != NULL) {
\r
6253 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
6259 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
6261 char buf1[MAX_ARG_LEN];
\r
6264 if (str[0] == '@') {
\r
6265 FILE* f = fopen(str + 1, "r");
\r
6267 DisplayFatalError(str + 1, errno, 2);
\r
6270 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
6272 buf1[len] = NULLCHAR;
\r
6277 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6280 char buf[MSG_SIZ];
\r
6281 char *end = strchr(str, '\n');
\r
6282 if (end == NULL) return;
\r
6283 memcpy(buf, str, end - str);
\r
6284 buf[end - str] = NULLCHAR;
\r
6285 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6291 SetStartupDialogEnables(HWND hDlg)
\r
6293 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6294 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6295 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6296 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6297 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6298 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6299 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6300 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6301 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6302 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6303 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6304 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6305 IsDlgButtonChecked(hDlg, OPT_View));
\r
6309 QuoteForFilename(char *filename)
\r
6311 int dquote, space;
\r
6312 dquote = strchr(filename, '"') != NULL;
\r
6313 space = strchr(filename, ' ') != NULL;
\r
6314 if (dquote || space) {
\r
6326 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6328 char buf[MSG_SIZ];
\r
6331 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6332 q = QuoteForFilename(nthcp);
\r
6333 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6334 if (*nthdir != NULLCHAR) {
\r
6335 q = QuoteForFilename(nthdir);
\r
6336 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6338 if (*nthcp == NULLCHAR) {
\r
6339 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6340 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6341 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6342 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6347 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6349 char buf[MSG_SIZ];
\r
6353 switch (message) {
\r
6354 case WM_INITDIALOG:
\r
6355 /* Center the dialog */
\r
6356 CenterWindow (hDlg, GetDesktopWindow());
\r
6357 Translate(hDlg, DLG_Startup);
\r
6358 /* Initialize the dialog items */
\r
6359 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6360 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6361 firstChessProgramNames);
\r
6362 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6363 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6364 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6365 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6366 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6367 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6368 if (*appData.icsHelper != NULLCHAR) {
\r
6369 char *q = QuoteForFilename(appData.icsHelper);
\r
6370 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6372 if (*appData.icsHost == NULLCHAR) {
\r
6373 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6374 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6375 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6376 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6377 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6380 if (appData.icsActive) {
\r
6381 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6383 else if (appData.noChessProgram) {
\r
6384 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6387 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6390 SetStartupDialogEnables(hDlg);
\r
6394 switch (LOWORD(wParam)) {
\r
6396 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6397 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6398 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6400 comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox
\r
6401 ParseArgs(StringGet, &p);
\r
6402 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6403 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6405 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6406 ParseArgs(StringGet, &p);
\r
6407 SwapEngines(singleList); // ... and then make it 'second'
\r
6409 appData.noChessProgram = FALSE;
\r
6410 appData.icsActive = FALSE;
\r
6411 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6412 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6413 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6415 ParseArgs(StringGet, &p);
\r
6416 if (appData.zippyPlay) {
\r
6417 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6418 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6420 ParseArgs(StringGet, &p);
\r
6422 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6423 appData.noChessProgram = TRUE;
\r
6424 appData.icsActive = FALSE;
\r
6426 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6427 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6430 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6431 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6433 ParseArgs(StringGet, &p);
\r
6435 EndDialog(hDlg, TRUE);
\r
6442 case IDM_HELPCONTENTS:
\r
6443 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6444 MessageBox (GetFocus(),
\r
6445 _("Unable to activate help"),
\r
6446 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6451 SetStartupDialogEnables(hDlg);
\r
6459 /*---------------------------------------------------------------------------*\
\r
6461 * About box dialog functions
\r
6463 \*---------------------------------------------------------------------------*/
\r
6465 /* Process messages for "About" dialog box */
\r
6467 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6469 switch (message) {
\r
6470 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6471 /* Center the dialog over the application window */
\r
6472 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6473 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6474 Translate(hDlg, ABOUTBOX);
\r
6478 case WM_COMMAND: /* message: received a command */
\r
6479 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6480 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6481 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6489 /*---------------------------------------------------------------------------*\
\r
6491 * Comment Dialog functions
\r
6493 \*---------------------------------------------------------------------------*/
\r
6496 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6498 static HANDLE hwndText = NULL;
\r
6499 int len, newSizeX, newSizeY;
\r
6500 static int sizeX, sizeY;
\r
6505 switch (message) {
\r
6506 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6507 /* Initialize the dialog items */
\r
6508 Translate(hDlg, DLG_EditComment);
\r
6509 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6510 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6511 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6512 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6513 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6514 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6515 SetWindowText(hDlg, commentTitle);
\r
6516 if (editComment) {
\r
6517 SetFocus(hwndText);
\r
6519 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6521 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6522 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6523 MAKELPARAM(FALSE, 0));
\r
6524 /* Size and position the dialog */
\r
6525 if (!commentDialog) {
\r
6526 commentDialog = hDlg;
\r
6527 GetClientRect(hDlg, &rect);
\r
6528 sizeX = rect.right;
\r
6529 sizeY = rect.bottom;
\r
6530 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6531 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6532 WINDOWPLACEMENT wp;
\r
6533 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6534 wp.length = sizeof(WINDOWPLACEMENT);
\r
6536 wp.showCmd = SW_SHOW;
\r
6537 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6538 wp.rcNormalPosition.left = wpComment.x;
\r
6539 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6540 wp.rcNormalPosition.top = wpComment.y;
\r
6541 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6542 SetWindowPlacement(hDlg, &wp);
\r
6544 GetClientRect(hDlg, &rect);
\r
6545 newSizeX = rect.right;
\r
6546 newSizeY = rect.bottom;
\r
6547 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6548 newSizeX, newSizeY);
\r
6553 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6556 case WM_COMMAND: /* message: received a command */
\r
6557 switch (LOWORD(wParam)) {
\r
6559 if (editComment) {
\r
6561 /* Read changed options from the dialog box */
\r
6562 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6563 len = GetWindowTextLength(hwndText);
\r
6564 str = (char *) malloc(len + 1);
\r
6565 GetWindowText(hwndText, str, len + 1);
\r
6574 ReplaceComment(commentIndex, str);
\r
6581 case OPT_CancelComment:
\r
6585 case OPT_ClearComment:
\r
6586 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6589 case OPT_EditComment:
\r
6590 EditCommentEvent();
\r
6598 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6599 if( wParam == OPT_CommentText ) {
\r
6600 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6602 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6603 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6607 pt.x = LOWORD( lpMF->lParam );
\r
6608 pt.y = HIWORD( lpMF->lParam );
\r
6610 if(lpMF->msg == WM_CHAR) {
\r
6612 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6613 index = sel.cpMin;
\r
6615 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6617 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6618 len = GetWindowTextLength(hwndText);
\r
6619 str = (char *) malloc(len + 1);
\r
6620 GetWindowText(hwndText, str, len + 1);
\r
6621 ReplaceComment(commentIndex, str);
\r
6622 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6623 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6626 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6627 lpMF->msg = WM_USER;
\r
6635 newSizeX = LOWORD(lParam);
\r
6636 newSizeY = HIWORD(lParam);
\r
6637 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6642 case WM_GETMINMAXINFO:
\r
6643 /* Prevent resizing window too small */
\r
6644 mmi = (MINMAXINFO *) lParam;
\r
6645 mmi->ptMinTrackSize.x = 100;
\r
6646 mmi->ptMinTrackSize.y = 100;
\r
6653 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6658 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6660 if (str == NULL) str = "";
\r
6661 p = (char *) malloc(2 * strlen(str) + 2);
\r
6664 if (*str == '\n') *q++ = '\r';
\r
6668 if (commentText != NULL) free(commentText);
\r
6670 commentIndex = index;
\r
6671 commentTitle = title;
\r
6673 editComment = edit;
\r
6675 if (commentDialog) {
\r
6676 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6677 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6679 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6680 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6681 hwndMain, (DLGPROC)lpProc);
\r
6682 FreeProcInstance(lpProc);
\r
6688 /*---------------------------------------------------------------------------*\
\r
6690 * Type-in move dialog functions
\r
6692 \*---------------------------------------------------------------------------*/
\r
6695 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6697 char move[MSG_SIZ];
\r
6700 switch (message) {
\r
6701 case WM_INITDIALOG:
\r
6702 move[0] = (char) lParam;
\r
6703 move[1] = NULLCHAR;
\r
6704 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6705 Translate(hDlg, DLG_TypeInMove);
\r
6706 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6707 SetWindowText(hInput, move);
\r
6709 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6713 switch (LOWORD(wParam)) {
\r
6716 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6717 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6718 TypeInDoneEvent(move);
\r
6719 EndDialog(hDlg, TRUE);
\r
6722 EndDialog(hDlg, FALSE);
\r
6733 PopUpMoveDialog(char firstchar)
\r
6737 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6738 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6739 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6740 FreeProcInstance(lpProc);
\r
6743 /*---------------------------------------------------------------------------*\
\r
6745 * Type-in name dialog functions
\r
6747 \*---------------------------------------------------------------------------*/
\r
6750 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6752 char move[MSG_SIZ];
\r
6755 switch (message) {
\r
6756 case WM_INITDIALOG:
\r
6757 move[0] = (char) lParam;
\r
6758 move[1] = NULLCHAR;
\r
6759 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6760 Translate(hDlg, DLG_TypeInName);
\r
6761 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6762 SetWindowText(hInput, move);
\r
6764 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6768 switch (LOWORD(wParam)) {
\r
6770 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6771 appData.userName = strdup(move);
\r
6772 SetUserLogo(); DisplayLogos();
\r
6774 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6775 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6776 DisplayTitle(move);
\r
6780 EndDialog(hDlg, TRUE);
\r
6783 EndDialog(hDlg, FALSE);
\r
6794 PopUpNameDialog(char firstchar)
\r
6798 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6799 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6800 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6801 FreeProcInstance(lpProc);
\r
6804 /*---------------------------------------------------------------------------*\
\r
6808 \*---------------------------------------------------------------------------*/
\r
6810 /* Nonmodal error box */
\r
6811 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6812 WPARAM wParam, LPARAM lParam);
\r
6815 ErrorPopUp(char *title, char *content)
\r
6819 BOOLEAN modal = hwndMain == NULL;
\r
6837 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6838 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6841 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6843 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6844 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6845 hwndMain, (DLGPROC)lpProc);
\r
6846 FreeProcInstance(lpProc);
\r
6853 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6854 if (errorDialog == NULL) return;
\r
6855 DestroyWindow(errorDialog);
\r
6856 errorDialog = NULL;
\r
6857 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6861 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6865 switch (message) {
\r
6866 case WM_INITDIALOG:
\r
6867 GetWindowRect(hDlg, &rChild);
\r
6870 SetWindowPos(hDlg, NULL, rChild.left,
\r
6871 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6872 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6876 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6877 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6878 and it doesn't work when you resize the dialog.
\r
6879 For now, just give it a default position.
\r
6881 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6882 Translate(hDlg, DLG_Error);
\r
6884 errorDialog = hDlg;
\r
6885 SetWindowText(hDlg, errorTitle);
\r
6886 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6890 switch (LOWORD(wParam)) {
\r
6893 if (errorDialog == hDlg) errorDialog = NULL;
\r
6894 DestroyWindow(hDlg);
\r
6906 HWND gothicDialog = NULL;
\r
6909 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6912 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6914 switch (message) {
\r
6915 case WM_INITDIALOG:
\r
6916 GetWindowRect(hDlg, &rChild);
\r
6918 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6922 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6923 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6924 and it doesn't work when you resize the dialog.
\r
6925 For now, just give it a default position.
\r
6927 gothicDialog = hDlg;
\r
6928 SetWindowText(hDlg, errorTitle);
\r
6929 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6933 switch (LOWORD(wParam)) {
\r
6936 if (errorDialog == hDlg) errorDialog = NULL;
\r
6937 DestroyWindow(hDlg);
\r
6949 GothicPopUp(char *title, VariantClass variant)
\r
6952 static char *lastTitle;
\r
6954 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6955 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6957 if(lastTitle != title && gothicDialog != NULL) {
\r
6958 DestroyWindow(gothicDialog);
\r
6959 gothicDialog = NULL;
\r
6961 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6962 title = lastTitle;
\r
6963 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6964 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6965 hwndMain, (DLGPROC)lpProc);
\r
6966 FreeProcInstance(lpProc);
\r
6971 /*---------------------------------------------------------------------------*\
\r
6973 * Ics Interaction console functions
\r
6975 \*---------------------------------------------------------------------------*/
\r
6977 #define HISTORY_SIZE 64
\r
6978 static char *history[HISTORY_SIZE];
\r
6979 int histIn = 0, histP = 0;
\r
6983 SaveInHistory(char *cmd)
\r
6985 if (history[histIn] != NULL) {
\r
6986 free(history[histIn]);
\r
6987 history[histIn] = NULL;
\r
6989 if (*cmd == NULLCHAR) return;
\r
6990 history[histIn] = StrSave(cmd);
\r
6991 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6992 if (history[histIn] != NULL) {
\r
6993 free(history[histIn]);
\r
6995 history[histIn] = NULL;
\r
7001 PrevInHistory(char *cmd)
\r
7004 if (histP == histIn) {
\r
7005 if (history[histIn] != NULL) free(history[histIn]);
\r
7006 history[histIn] = StrSave(cmd);
\r
7008 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
7009 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
7011 return history[histP];
\r
7017 if (histP == histIn) return NULL;
\r
7018 histP = (histP + 1) % HISTORY_SIZE;
\r
7019 return history[histP];
\r
7023 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
7027 hmenu = LoadMenu(hInst, "TextMenu");
\r
7028 h = GetSubMenu(hmenu, 0);
\r
7030 if (strcmp(e->item, "-") == 0) {
\r
7031 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
7032 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
7033 int flags = MF_STRING, j = 0;
\r
7034 if (e->item[0] == '|') {
\r
7035 flags |= MF_MENUBARBREAK;
\r
7038 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
7039 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
7047 WNDPROC consoleTextWindowProc;
\r
7050 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
7052 char buf[MSG_SIZ], name[MSG_SIZ];
\r
7053 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7057 SetWindowText(hInput, command);
\r
7059 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
7061 sel.cpMin = 999999;
\r
7062 sel.cpMax = 999999;
\r
7063 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7068 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7069 if (sel.cpMin == sel.cpMax) {
\r
7070 /* Expand to surrounding word */
\r
7073 tr.chrg.cpMax = sel.cpMin;
\r
7074 tr.chrg.cpMin = --sel.cpMin;
\r
7075 if (sel.cpMin < 0) break;
\r
7076 tr.lpstrText = name;
\r
7077 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
7078 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
7082 tr.chrg.cpMin = sel.cpMax;
\r
7083 tr.chrg.cpMax = ++sel.cpMax;
\r
7084 tr.lpstrText = name;
\r
7085 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
7086 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
7089 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
7090 MessageBeep(MB_ICONEXCLAMATION);
\r
7094 tr.lpstrText = name;
\r
7095 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
7097 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
7098 MessageBeep(MB_ICONEXCLAMATION);
\r
7101 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
7104 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
7105 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
7106 SetWindowText(hInput, buf);
\r
7107 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
7109 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
7110 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
7111 SetWindowText(hInput, buf);
\r
7112 sel.cpMin = 999999;
\r
7113 sel.cpMax = 999999;
\r
7114 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7120 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7125 switch (message) {
\r
7127 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7128 if(wParam=='R') return 0;
\r
7131 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
7134 sel.cpMin = 999999;
\r
7135 sel.cpMax = 999999;
\r
7136 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7137 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
7142 if(wParam != '\022') {
\r
7143 if (wParam == '\t') {
\r
7144 if (GetKeyState(VK_SHIFT) < 0) {
\r
7146 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7147 if (buttonDesc[0].hwnd) {
\r
7148 SetFocus(buttonDesc[0].hwnd);
\r
7150 SetFocus(hwndMain);
\r
7154 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
7157 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7158 JAWS_DELETE( SetFocus(hInput); )
\r
7159 SendMessage(hInput, message, wParam, lParam);
\r
7162 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
7164 case WM_RBUTTONDOWN:
\r
7165 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
7166 /* Move selection here if it was empty */
\r
7168 pt.x = LOWORD(lParam);
\r
7169 pt.y = HIWORD(lParam);
\r
7170 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7171 if (sel.cpMin == sel.cpMax) {
\r
7172 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
7173 sel.cpMax = sel.cpMin;
\r
7174 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7176 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
7177 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
7179 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
7180 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7181 if (sel.cpMin == sel.cpMax) {
\r
7182 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7183 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
7185 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7186 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7188 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
7189 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
7190 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
7191 MenuPopup(hwnd, pt, hmenu, -1);
\r
7195 case WM_RBUTTONUP:
\r
7196 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7197 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7198 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7202 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7204 return SendMessage(hInput, message, wParam, lParam);
\r
7205 case WM_MBUTTONDOWN:
\r
7206 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7208 switch (LOWORD(wParam)) {
\r
7209 case IDM_QuickPaste:
\r
7211 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7212 if (sel.cpMin == sel.cpMax) {
\r
7213 MessageBeep(MB_ICONEXCLAMATION);
\r
7216 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7217 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7218 SendMessage(hInput, WM_PASTE, 0, 0);
\r
7223 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7226 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7229 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7233 int i = LOWORD(wParam) - IDM_CommandX;
\r
7234 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
7235 icsTextMenuEntry[i].command != NULL) {
\r
7236 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
7237 icsTextMenuEntry[i].getname,
\r
7238 icsTextMenuEntry[i].immediate);
\r
7246 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
7249 WNDPROC consoleInputWindowProc;
\r
7252 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7254 char buf[MSG_SIZ];
\r
7256 static BOOL sendNextChar = FALSE;
\r
7257 static BOOL quoteNextChar = FALSE;
\r
7258 InputSource *is = consoleInputSource;
\r
7262 switch (message) {
\r
7264 if (!appData.localLineEditing || sendNextChar) {
\r
7265 is->buf[0] = (CHAR) wParam;
\r
7267 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7268 sendNextChar = FALSE;
\r
7271 if (quoteNextChar) {
\r
7272 buf[0] = (char) wParam;
\r
7273 buf[1] = NULLCHAR;
\r
7274 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7275 quoteNextChar = FALSE;
\r
7279 case '\r': /* Enter key */
\r
7280 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7281 if (consoleEcho) SaveInHistory(is->buf);
\r
7282 is->buf[is->count++] = '\n';
\r
7283 is->buf[is->count] = NULLCHAR;
\r
7284 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7285 if (consoleEcho) {
\r
7286 ConsoleOutput(is->buf, is->count, TRUE);
\r
7287 } else if (appData.localLineEditing) {
\r
7288 ConsoleOutput("\n", 1, TRUE);
\r
7291 case '\033': /* Escape key */
\r
7292 SetWindowText(hwnd, "");
\r
7293 cf.cbSize = sizeof(CHARFORMAT);
\r
7294 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7295 if (consoleEcho) {
\r
7296 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7298 cf.crTextColor = COLOR_ECHOOFF;
\r
7300 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7301 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7303 case '\t': /* Tab key */
\r
7304 if (GetKeyState(VK_SHIFT) < 0) {
\r
7306 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7309 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7310 if (buttonDesc[0].hwnd) {
\r
7311 SetFocus(buttonDesc[0].hwnd);
\r
7313 SetFocus(hwndMain);
\r
7317 case '\023': /* Ctrl+S */
\r
7318 sendNextChar = TRUE;
\r
7320 case '\021': /* Ctrl+Q */
\r
7321 quoteNextChar = TRUE;
\r
7331 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7332 p = PrevInHistory(buf);
\r
7334 SetWindowText(hwnd, p);
\r
7335 sel.cpMin = 999999;
\r
7336 sel.cpMax = 999999;
\r
7337 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7342 p = NextInHistory();
\r
7344 SetWindowText(hwnd, p);
\r
7345 sel.cpMin = 999999;
\r
7346 sel.cpMax = 999999;
\r
7347 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7353 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7357 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7361 case WM_MBUTTONDOWN:
\r
7362 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7363 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7365 case WM_RBUTTONUP:
\r
7366 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7367 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7368 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7372 hmenu = LoadMenu(hInst, "InputMenu");
\r
7373 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7374 if (sel.cpMin == sel.cpMax) {
\r
7375 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7376 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7378 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7379 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7381 pt.x = LOWORD(lParam);
\r
7382 pt.y = HIWORD(lParam);
\r
7383 MenuPopup(hwnd, pt, hmenu, -1);
\r
7387 switch (LOWORD(wParam)) {
\r
7389 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7391 case IDM_SelectAll:
\r
7393 sel.cpMax = -1; /*999999?*/
\r
7394 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7397 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7400 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7403 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7408 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7411 #define CO_MAX 100000
\r
7412 #define CO_TRIM 1000
\r
7415 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7417 static SnapData sd;
\r
7418 HWND hText, hInput;
\r
7420 static int sizeX, sizeY;
\r
7421 int newSizeX, newSizeY;
\r
7425 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7426 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7428 switch (message) {
\r
7430 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7432 ENLINK *pLink = (ENLINK*)lParam;
\r
7433 if (pLink->msg == WM_LBUTTONUP)
\r
7437 tr.chrg = pLink->chrg;
\r
7438 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7439 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7440 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7441 free(tr.lpstrText);
\r
7445 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7446 hwndConsole = hDlg;
\r
7448 consoleTextWindowProc = (WNDPROC)
\r
7449 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7450 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7451 consoleInputWindowProc = (WNDPROC)
\r
7452 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7453 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7454 Colorize(ColorNormal, TRUE);
\r
7455 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7456 ChangedConsoleFont();
\r
7457 GetClientRect(hDlg, &rect);
\r
7458 sizeX = rect.right;
\r
7459 sizeY = rect.bottom;
\r
7460 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7461 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7462 WINDOWPLACEMENT wp;
\r
7463 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7464 wp.length = sizeof(WINDOWPLACEMENT);
\r
7466 wp.showCmd = SW_SHOW;
\r
7467 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7468 wp.rcNormalPosition.left = wpConsole.x;
\r
7469 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7470 wp.rcNormalPosition.top = wpConsole.y;
\r
7471 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7472 SetWindowPlacement(hDlg, &wp);
\r
7475 // [HGM] Chessknight's change 2004-07-13
\r
7476 else { /* Determine Defaults */
\r
7477 WINDOWPLACEMENT wp;
\r
7478 wpConsole.x = wpMain.width + 1;
\r
7479 wpConsole.y = wpMain.y;
\r
7480 wpConsole.width = screenWidth - wpMain.width;
\r
7481 wpConsole.height = wpMain.height;
\r
7482 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7483 wp.length = sizeof(WINDOWPLACEMENT);
\r
7485 wp.showCmd = SW_SHOW;
\r
7486 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7487 wp.rcNormalPosition.left = wpConsole.x;
\r
7488 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7489 wp.rcNormalPosition.top = wpConsole.y;
\r
7490 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7491 SetWindowPlacement(hDlg, &wp);
\r
7494 // Allow hText to highlight URLs and send notifications on them
\r
7495 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7496 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7497 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7498 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7512 if (IsIconic(hDlg)) break;
\r
7513 newSizeX = LOWORD(lParam);
\r
7514 newSizeY = HIWORD(lParam);
\r
7515 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7516 RECT rectText, rectInput;
\r
7518 int newTextHeight, newTextWidth;
\r
7519 GetWindowRect(hText, &rectText);
\r
7520 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7521 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7522 if (newTextHeight < 0) {
\r
7523 newSizeY += -newTextHeight;
\r
7524 newTextHeight = 0;
\r
7526 SetWindowPos(hText, NULL, 0, 0,
\r
7527 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7528 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7529 pt.x = rectInput.left;
\r
7530 pt.y = rectInput.top + newSizeY - sizeY;
\r
7531 ScreenToClient(hDlg, &pt);
\r
7532 SetWindowPos(hInput, NULL,
\r
7533 pt.x, pt.y, /* needs client coords */
\r
7534 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7535 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7541 case WM_GETMINMAXINFO:
\r
7542 /* Prevent resizing window too small */
\r
7543 mmi = (MINMAXINFO *) lParam;
\r
7544 mmi->ptMinTrackSize.x = 100;
\r
7545 mmi->ptMinTrackSize.y = 100;
\r
7548 /* [AS] Snapping */
\r
7549 case WM_ENTERSIZEMOVE:
\r
7550 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7553 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7556 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7558 case WM_EXITSIZEMOVE:
\r
7559 UpdateICSWidth(hText);
\r
7560 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7563 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7571 if (hwndConsole) return;
\r
7572 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7573 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7578 ConsoleOutput(char* data, int length, int forceVisible)
\r
7583 char buf[CO_MAX+1];
\r
7586 static int delayLF = 0;
\r
7587 CHARRANGE savesel, sel;
\r
7589 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7597 while (length--) {
\r
7605 } else if (*p == '\007') {
\r
7606 MyPlaySound(&sounds[(int)SoundBell]);
\r
7613 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7614 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7615 /* Save current selection */
\r
7616 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7617 exlen = GetWindowTextLength(hText);
\r
7618 /* Find out whether current end of text is visible */
\r
7619 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7620 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7621 /* Trim existing text if it's too long */
\r
7622 if (exlen + (q - buf) > CO_MAX) {
\r
7623 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7626 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7627 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7629 savesel.cpMin -= trim;
\r
7630 savesel.cpMax -= trim;
\r
7631 if (exlen < 0) exlen = 0;
\r
7632 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7633 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7635 /* Append the new text */
\r
7636 sel.cpMin = exlen;
\r
7637 sel.cpMax = exlen;
\r
7638 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7639 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7640 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7641 if (forceVisible || exlen == 0 ||
\r
7642 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7643 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7644 /* Scroll to make new end of text visible if old end of text
\r
7645 was visible or new text is an echo of user typein */
\r
7646 sel.cpMin = 9999999;
\r
7647 sel.cpMax = 9999999;
\r
7648 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7649 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7650 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7651 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7653 if (savesel.cpMax == exlen || forceVisible) {
\r
7654 /* Move insert point to new end of text if it was at the old
\r
7655 end of text or if the new text is an echo of user typein */
\r
7656 sel.cpMin = 9999999;
\r
7657 sel.cpMax = 9999999;
\r
7658 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7660 /* Restore previous selection */
\r
7661 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7663 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7670 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7674 COLORREF oldFg, oldBg;
\r
7678 if(copyNumber > 1)
\r
7679 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7681 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7682 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7683 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7686 rect.right = x + squareSize;
\r
7688 rect.bottom = y + squareSize;
\r
7691 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7692 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7693 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7694 &rect, str, strlen(str), NULL);
\r
7696 (void) SetTextColor(hdc, oldFg);
\r
7697 (void) SetBkColor(hdc, oldBg);
\r
7698 (void) SelectObject(hdc, oldFont);
\r
7702 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7703 RECT *rect, char *color, char *flagFell)
\r
7707 COLORREF oldFg, oldBg;
\r
7710 if (twoBoards && partnerUp) return;
\r
7711 if (appData.clockMode) {
\r
7712 if (tinyLayout == 2)
\r
7713 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7715 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7722 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7723 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7725 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7726 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7729 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7733 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7734 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7735 rect, str, strlen(str), NULL);
\r
7736 if(logoHeight > 0 && appData.clockMode) {
\r
7738 str += strlen(color)+2;
\r
7739 r.top = rect->top + logoHeight/2;
\r
7740 r.left = rect->left;
\r
7741 r.right = rect->right;
\r
7742 r.bottom = rect->bottom;
\r
7743 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7744 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7745 &r, str, strlen(str), NULL);
\r
7747 (void) SetTextColor(hdc, oldFg);
\r
7748 (void) SetBkColor(hdc, oldBg);
\r
7749 (void) SelectObject(hdc, oldFont);
\r
7754 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7760 if( count <= 0 ) {
\r
7761 if (appData.debugMode) {
\r
7762 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7765 return ERROR_INVALID_USER_BUFFER;
\r
7768 ResetEvent(ovl->hEvent);
\r
7769 ovl->Offset = ovl->OffsetHigh = 0;
\r
7770 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7774 err = GetLastError();
\r
7775 if (err == ERROR_IO_PENDING) {
\r
7776 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7780 err = GetLastError();
\r
7787 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7792 ResetEvent(ovl->hEvent);
\r
7793 ovl->Offset = ovl->OffsetHigh = 0;
\r
7794 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7798 err = GetLastError();
\r
7799 if (err == ERROR_IO_PENDING) {
\r
7800 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7804 err = GetLastError();
\r
7811 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7812 void CheckForInputBufferFull( InputSource * is )
\r
7814 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7815 /* Look for end of line */
\r
7816 char * p = is->buf;
\r
7818 while( p < is->next && *p != '\n' ) {
\r
7822 if( p >= is->next ) {
\r
7823 if (appData.debugMode) {
\r
7824 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7827 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7828 is->count = (DWORD) -1;
\r
7829 is->next = is->buf;
\r
7835 InputThread(LPVOID arg)
\r
7840 is = (InputSource *) arg;
\r
7841 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7842 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7843 while (is->hThread != NULL) {
\r
7844 is->error = DoReadFile(is->hFile, is->next,
\r
7845 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7846 &is->count, &ovl);
\r
7847 if (is->error == NO_ERROR) {
\r
7848 is->next += is->count;
\r
7850 if (is->error == ERROR_BROKEN_PIPE) {
\r
7851 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7854 is->count = (DWORD) -1;
\r
7855 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7860 CheckForInputBufferFull( is );
\r
7862 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7864 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7866 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7869 CloseHandle(ovl.hEvent);
\r
7870 CloseHandle(is->hFile);
\r
7872 if (appData.debugMode) {
\r
7873 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7880 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7882 NonOvlInputThread(LPVOID arg)
\r
7889 is = (InputSource *) arg;
\r
7890 while (is->hThread != NULL) {
\r
7891 is->error = ReadFile(is->hFile, is->next,
\r
7892 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7893 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7894 if (is->error == NO_ERROR) {
\r
7895 /* Change CRLF to LF */
\r
7896 if (is->next > is->buf) {
\r
7898 i = is->count + 1;
\r
7906 if (prev == '\r' && *p == '\n') {
\r
7918 if (is->error == ERROR_BROKEN_PIPE) {
\r
7919 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7922 is->count = (DWORD) -1;
\r
7926 CheckForInputBufferFull( is );
\r
7928 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7930 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7932 if (is->count < 0) break; /* Quit on error */
\r
7934 CloseHandle(is->hFile);
\r
7939 SocketInputThread(LPVOID arg)
\r
7943 is = (InputSource *) arg;
\r
7944 while (is->hThread != NULL) {
\r
7945 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7946 if ((int)is->count == SOCKET_ERROR) {
\r
7947 is->count = (DWORD) -1;
\r
7948 is->error = WSAGetLastError();
\r
7950 is->error = NO_ERROR;
\r
7951 is->next += is->count;
\r
7952 if (is->count == 0 && is->second == is) {
\r
7953 /* End of file on stderr; quit with no message */
\r
7957 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7959 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7961 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7967 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7971 is = (InputSource *) lParam;
\r
7972 if (is->lineByLine) {
\r
7973 /* Feed in lines one by one */
\r
7974 char *p = is->buf;
\r
7976 while (q < is->next) {
\r
7977 if (*q++ == '\n') {
\r
7978 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7983 /* Move any partial line to the start of the buffer */
\r
7985 while (p < is->next) {
\r
7990 if (is->error != NO_ERROR || is->count == 0) {
\r
7991 /* Notify backend of the error. Note: If there was a partial
\r
7992 line at the end, it is not flushed through. */
\r
7993 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7996 /* Feed in the whole chunk of input at once */
\r
7997 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7998 is->next = is->buf;
\r
8002 /*---------------------------------------------------------------------------*\
\r
8004 * Menu enables. Used when setting various modes.
\r
8006 \*---------------------------------------------------------------------------*/
\r
8014 GreyRevert(Boolean grey)
\r
8015 { // [HGM] vari: for retracting variations in local mode
\r
8016 HMENU hmenu = GetMenu(hwndMain);
\r
8017 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
8018 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
8022 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
8024 while (enab->item > 0) {
\r
8025 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
8030 Enables gnuEnables[] = {
\r
8031 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
8032 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
8033 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
8034 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
8035 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
8036 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
8037 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
8038 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
8039 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
8040 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
8041 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
8042 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
8043 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
8046 // Needed to switch from ncp to GNU mode on Engine Load
\r
8047 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
8048 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
8049 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
8050 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
8051 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
8052 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
8053 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
8054 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
8055 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
8056 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
8057 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
8058 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8059 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
8060 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
8064 Enables icsEnables[] = {
\r
8065 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
8066 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
8067 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8068 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8069 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8070 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8071 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
8072 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
8073 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
8074 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
8075 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8076 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
8077 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
8078 { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },
\r
8079 { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },
\r
8080 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
8081 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
8082 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
8083 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
8084 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
8089 Enables zippyEnables[] = {
\r
8090 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8091 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
8092 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
8093 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
8098 Enables ncpEnables[] = {
\r
8099 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
8100 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
8101 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8102 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8103 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8104 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8105 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
8106 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
8107 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
8108 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
8109 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
8110 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
8111 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8112 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
8113 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
8114 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
8115 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
8116 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
8117 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
8118 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
8119 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
8120 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
8124 Enables trainingOnEnables[] = {
\r
8125 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
8126 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
8127 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
8128 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
8129 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
8130 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
8131 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
8132 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8133 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
8137 Enables trainingOffEnables[] = {
\r
8138 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
8139 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
8140 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
8141 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
8142 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
8143 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
8144 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
8145 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8146 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
8150 /* These modify either ncpEnables or gnuEnables */
\r
8151 Enables cmailEnables[] = {
\r
8152 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
8153 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
8154 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
8155 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
8156 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
8157 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
8158 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
8162 Enables machineThinkingEnables[] = {
\r
8163 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8164 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
8165 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
8166 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8167 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
8168 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8169 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8170 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8171 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8172 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
8173 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8174 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8175 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8176 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8177 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
8178 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
8182 Enables userThinkingEnables[] = {
\r
8183 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8184 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
8185 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
8186 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8187 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
8188 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8189 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8190 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8191 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8192 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
8193 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
8194 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
8195 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
8196 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
8197 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
8198 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
8202 /*---------------------------------------------------------------------------*\
\r
8204 * Front-end interface functions exported by XBoard.
\r
8205 * Functions appear in same order as prototypes in frontend.h.
\r
8207 \*---------------------------------------------------------------------------*/
\r
8209 CheckMark(UINT item, int state)
\r
8211 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
8217 static UINT prevChecked = 0;
\r
8218 static int prevPausing = 0;
\r
8221 if (pausing != prevPausing) {
\r
8222 prevPausing = pausing;
\r
8223 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
8224 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
8225 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
8228 switch (gameMode) {
\r
8229 case BeginningOfGame:
\r
8230 if (appData.icsActive)
\r
8231 nowChecked = IDM_IcsClient;
\r
8232 else if (appData.noChessProgram)
\r
8233 nowChecked = IDM_EditGame;
\r
8235 nowChecked = IDM_MachineBlack;
\r
8237 case MachinePlaysBlack:
\r
8238 nowChecked = IDM_MachineBlack;
\r
8240 case MachinePlaysWhite:
\r
8241 nowChecked = IDM_MachineWhite;
\r
8243 case TwoMachinesPlay:
\r
8244 nowChecked = IDM_TwoMachines;
\r
8247 nowChecked = IDM_AnalysisMode;
\r
8250 nowChecked = IDM_AnalyzeFile;
\r
8253 nowChecked = IDM_EditGame;
\r
8255 case PlayFromGameFile:
\r
8256 nowChecked = IDM_LoadGame;
\r
8258 case EditPosition:
\r
8259 nowChecked = IDM_EditPosition;
\r
8262 nowChecked = IDM_Training;
\r
8264 case IcsPlayingWhite:
\r
8265 case IcsPlayingBlack:
\r
8266 case IcsObserving:
\r
8268 nowChecked = IDM_IcsClient;
\r
8275 if(prevChecked == IDM_TwoMachines) // [HGM] 'Machine Match' might have gotten disabled when stopping match
\r
8276 EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_ENABLED);
\r
8277 CheckMark(prevChecked, MF_UNCHECKED);
\r
8278 CheckMark(nowChecked, MF_CHECKED);
\r
8279 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8281 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8282 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8283 MF_BYCOMMAND|MF_ENABLED);
\r
8285 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8286 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8289 prevChecked = nowChecked;
\r
8291 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8292 if (appData.icsActive) {
\r
8293 if (appData.icsEngineAnalyze) {
\r
8294 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8296 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8299 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8305 HMENU hmenu = GetMenu(hwndMain);
\r
8306 SetMenuEnables(hmenu, icsEnables);
\r
8307 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8308 MF_BYCOMMAND|MF_ENABLED);
\r
8310 if (appData.zippyPlay) {
\r
8311 SetMenuEnables(hmenu, zippyEnables);
\r
8312 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8313 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8314 MF_BYCOMMAND|MF_ENABLED);
\r
8322 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8328 HMENU hmenu = GetMenu(hwndMain);
\r
8329 SetMenuEnables(hmenu, ncpEnables);
\r
8330 DrawMenuBar(hwndMain);
\r
8336 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8340 SetTrainingModeOn()
\r
8343 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8344 for (i = 0; i < N_BUTTONS; i++) {
\r
8345 if (buttonDesc[i].hwnd != NULL)
\r
8346 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8351 VOID SetTrainingModeOff()
\r
8354 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8355 for (i = 0; i < N_BUTTONS; i++) {
\r
8356 if (buttonDesc[i].hwnd != NULL)
\r
8357 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8363 SetUserThinkingEnables()
\r
8365 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8369 SetMachineThinkingEnables()
\r
8371 HMENU hMenu = GetMenu(hwndMain);
\r
8372 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8374 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8376 if (gameMode == MachinePlaysBlack) {
\r
8377 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8378 } else if (gameMode == MachinePlaysWhite) {
\r
8379 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8380 } else if (gameMode == TwoMachinesPlay) {
\r
8381 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8387 DisplayTitle(char *str)
\r
8389 char title[MSG_SIZ], *host;
\r
8390 if (str[0] != NULLCHAR) {
\r
8391 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8392 } else if (appData.icsActive) {
\r
8393 if (appData.icsCommPort[0] != NULLCHAR)
\r
8396 host = appData.icsHost;
\r
8397 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8398 } else if (appData.noChessProgram) {
\r
8399 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8401 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8402 strcat(title, ": ");
\r
8403 strcat(title, first.tidy);
\r
8405 SetWindowText(hwndMain, title);
\r
8410 DisplayMessage(char *str1, char *str2)
\r
8414 int remain = MESSAGE_TEXT_MAX - 1;
\r
8417 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8418 messageText[0] = NULLCHAR;
\r
8420 len = strlen(str1);
\r
8421 if (len > remain) len = remain;
\r
8422 strncpy(messageText, str1, len);
\r
8423 messageText[len] = NULLCHAR;
\r
8426 if (*str2 && remain >= 2) {
\r
8428 strcat(messageText, " ");
\r
8431 len = strlen(str2);
\r
8432 if (len > remain) len = remain;
\r
8433 strncat(messageText, str2, len);
\r
8435 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8436 safeStrCpy(lastMsg, messageText, MSG_SIZ);
\r
8438 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8442 hdc = GetDC(hwndMain);
\r
8443 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8444 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8445 &messageRect, messageText, strlen(messageText), NULL);
\r
8446 (void) SelectObject(hdc, oldFont);
\r
8447 (void) ReleaseDC(hwndMain, hdc);
\r
8451 DisplayError(char *str, int error)
\r
8453 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8457 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8459 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8460 NULL, error, LANG_NEUTRAL,
\r
8461 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8463 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8465 ErrorMap *em = errmap;
\r
8466 while (em->err != 0 && em->err != error) em++;
\r
8467 if (em->err != 0) {
\r
8468 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8470 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8475 ErrorPopUp(_("Error"), buf);
\r
8480 DisplayMoveError(char *str)
\r
8482 fromX = fromY = -1;
\r
8483 ClearHighlights();
\r
8484 DrawPosition(FALSE, NULL);
\r
8485 if (appData.popupMoveErrors) {
\r
8486 ErrorPopUp(_("Error"), str);
\r
8488 DisplayMessage(str, "");
\r
8489 moveErrorMessageUp = TRUE;
\r
8494 DisplayFatalError(char *str, int error, int exitStatus)
\r
8496 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8498 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8501 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8502 NULL, error, LANG_NEUTRAL,
\r
8503 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8505 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8507 ErrorMap *em = errmap;
\r
8508 while (em->err != 0 && em->err != error) em++;
\r
8509 if (em->err != 0) {
\r
8510 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8512 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8517 if (appData.debugMode) {
\r
8518 fprintf(debugFP, "%s: %s\n", label, str);
\r
8520 if (appData.popupExitMessage) {
\r
8521 if(appData.icsActive) SendToICS("logout\n"); // [HGM] make sure no new games will be started!
\r
8522 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8523 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8525 ExitEvent(exitStatus);
\r
8530 DisplayInformation(char *str)
\r
8532 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8542 DisplayNote(char *str)
\r
8544 ErrorPopUp(_("Note"), str);
\r
8549 char *title, *question, *replyPrefix;
\r
8554 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8556 static QuestionParams *qp;
\r
8557 char reply[MSG_SIZ];
\r
8560 switch (message) {
\r
8561 case WM_INITDIALOG:
\r
8562 qp = (QuestionParams *) lParam;
\r
8563 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8564 Translate(hDlg, DLG_Question);
\r
8565 SetWindowText(hDlg, qp->title);
\r
8566 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8567 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8571 switch (LOWORD(wParam)) {
\r
8573 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8574 if (*reply) strcat(reply, " ");
\r
8575 len = strlen(reply);
\r
8576 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8577 strcat(reply, "\n");
\r
8578 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8579 EndDialog(hDlg, TRUE);
\r
8580 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8583 EndDialog(hDlg, FALSE);
\r
8594 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8596 QuestionParams qp;
\r
8600 qp.question = question;
\r
8601 qp.replyPrefix = replyPrefix;
\r
8603 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8604 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8605 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8606 FreeProcInstance(lpProc);
\r
8609 /* [AS] Pick FRC position */
\r
8610 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8612 static int * lpIndexFRC;
\r
8618 case WM_INITDIALOG:
\r
8619 lpIndexFRC = (int *) lParam;
\r
8621 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8622 Translate(hDlg, DLG_NewGameFRC);
\r
8624 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8625 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8626 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8627 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8632 switch( LOWORD(wParam) ) {
\r
8634 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8635 EndDialog( hDlg, 0 );
\r
8636 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8639 EndDialog( hDlg, 1 );
\r
8641 case IDC_NFG_Edit:
\r
8642 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8643 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8645 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8648 case IDC_NFG_Random:
\r
8649 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8650 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8663 int index = appData.defaultFrcPosition;
\r
8664 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8666 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8668 if( result == 0 ) {
\r
8669 appData.defaultFrcPosition = index;
\r
8675 /* [AS] Game list options. Refactored by HGM */
\r
8677 HWND gameListOptionsDialog;
\r
8679 // low-level front-end: clear text edit / list widget
\r
8684 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8687 // low-level front-end: clear text edit / list widget
\r
8689 GLT_DeSelectList()
\r
8691 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8694 // low-level front-end: append line to text edit / list widget
\r
8696 GLT_AddToList( char *name )
\r
8699 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8703 // low-level front-end: get line from text edit / list widget
\r
8705 GLT_GetFromList( int index, char *name )
\r
8708 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8714 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8716 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8717 int idx2 = idx1 + delta;
\r
8718 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8720 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8723 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8724 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8725 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8726 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8730 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8734 case WM_INITDIALOG:
\r
8735 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8737 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8738 Translate(hDlg, DLG_GameListOptions);
\r
8740 /* Initialize list */
\r
8741 GLT_TagsToList( lpUserGLT );
\r
8743 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8748 switch( LOWORD(wParam) ) {
\r
8751 EndDialog( hDlg, 0 );
\r
8754 EndDialog( hDlg, 1 );
\r
8757 case IDC_GLT_Default:
\r
8758 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8761 case IDC_GLT_Restore:
\r
8762 GLT_TagsToList( appData.gameListTags );
\r
8766 GLT_MoveSelection( hDlg, -1 );
\r
8769 case IDC_GLT_Down:
\r
8770 GLT_MoveSelection( hDlg, +1 );
\r
8780 int GameListOptions()
\r
8783 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8785 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8787 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8789 if( result == 0 ) {
\r
8790 char *oldTags = appData.gameListTags;
\r
8791 /* [AS] Memory leak here! */
\r
8792 appData.gameListTags = strdup( lpUserGLT );
\r
8793 if(strcmp(oldTags, appData.gameListTags)) // [HGM] redo Game List when we changed something
\r
8794 GameListToListBox(NULL, TRUE, ".", NULL, FALSE, FALSE); // "." as filter is kludge to select all
\r
8801 DisplayIcsInteractionTitle(char *str)
\r
8803 char consoleTitle[MSG_SIZ];
\r
8805 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8806 SetWindowText(hwndConsole, consoleTitle);
\r
8808 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
8809 char buf[MSG_SIZ], *p = buf, *q;
\r
8810 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
8812 q = strchr(p, ';');
\r
8814 if(*p) ChatPopUp(p);
\r
8818 SetActiveWindow(hwndMain);
\r
8822 DrawPosition(int fullRedraw, Board board)
\r
8824 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8827 void NotifyFrontendLogin()
\r
8830 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8836 fromX = fromY = -1;
\r
8837 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8838 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8839 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8840 dragInfo.lastpos = dragInfo.pos;
\r
8841 dragInfo.start.x = dragInfo.start.y = -1;
\r
8842 dragInfo.from = dragInfo.start;
\r
8844 DrawPosition(TRUE, NULL);
\r
8851 CommentPopUp(char *title, char *str)
\r
8853 HWND hwnd = GetActiveWindow();
\r
8854 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8856 SetActiveWindow(hwnd);
\r
8860 CommentPopDown(void)
\r
8862 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8863 if (commentDialog) {
\r
8864 ShowWindow(commentDialog, SW_HIDE);
\r
8866 commentUp = FALSE;
\r
8870 EditCommentPopUp(int index, char *title, char *str)
\r
8872 EitherCommentPopUp(index, title, str, TRUE);
\r
8879 MyPlaySound(&sounds[(int)SoundRoar]);
\r
8886 MyPlaySound(&sounds[(int)SoundMove]);
\r
8889 VOID PlayIcsWinSound()
\r
8891 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8894 VOID PlayIcsLossSound()
\r
8896 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8899 VOID PlayIcsDrawSound()
\r
8901 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8904 VOID PlayIcsUnfinishedSound()
\r
8906 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8912 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8918 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8926 consoleEcho = TRUE;
\r
8927 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8928 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8929 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8938 consoleEcho = FALSE;
\r
8939 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8940 /* This works OK: set text and background both to the same color */
\r
8942 cf.crTextColor = COLOR_ECHOOFF;
\r
8943 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8944 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8947 /* No Raw()...? */
\r
8949 void Colorize(ColorClass cc, int continuation)
\r
8951 currentColorClass = cc;
\r
8952 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8953 consoleCF.crTextColor = textAttribs[cc].color;
\r
8954 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8955 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8961 static char buf[MSG_SIZ];
\r
8962 DWORD bufsiz = MSG_SIZ;
\r
8964 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8965 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8967 if (!GetUserName(buf, &bufsiz)) {
\r
8968 /*DisplayError("Error getting user name", GetLastError());*/
\r
8969 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8977 static char buf[MSG_SIZ];
\r
8978 DWORD bufsiz = MSG_SIZ;
\r
8980 if (!GetComputerName(buf, &bufsiz)) {
\r
8981 /*DisplayError("Error getting host name", GetLastError());*/
\r
8982 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8989 ClockTimerRunning()
\r
8991 return clockTimerEvent != 0;
\r
8997 if (clockTimerEvent == 0) return FALSE;
\r
8998 KillTimer(hwndMain, clockTimerEvent);
\r
8999 clockTimerEvent = 0;
\r
9004 StartClockTimer(long millisec)
\r
9006 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
9007 (UINT) millisec, NULL);
\r
9011 DisplayWhiteClock(long timeRemaining, int highlight)
\r
9014 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
9016 if(appData.noGUI) return;
\r
9017 hdc = GetDC(hwndMain);
\r
9018 if (!IsIconic(hwndMain)) {
\r
9019 DisplayAClock(hdc, timeRemaining, highlight,
\r
9020 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
9022 if (highlight && iconCurrent == iconBlack) {
\r
9023 iconCurrent = iconWhite;
\r
9024 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
9025 if (IsIconic(hwndMain)) {
\r
9026 DrawIcon(hdc, 2, 2, iconCurrent);
\r
9029 (void) ReleaseDC(hwndMain, hdc);
\r
9031 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
9035 DisplayBlackClock(long timeRemaining, int highlight)
\r
9038 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
9041 if(appData.noGUI) return;
\r
9042 hdc = GetDC(hwndMain);
\r
9043 if (!IsIconic(hwndMain)) {
\r
9044 DisplayAClock(hdc, timeRemaining, highlight,
\r
9045 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
9047 if (highlight && iconCurrent == iconWhite) {
\r
9048 iconCurrent = iconBlack;
\r
9049 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
9050 if (IsIconic(hwndMain)) {
\r
9051 DrawIcon(hdc, 2, 2, iconCurrent);
\r
9054 (void) ReleaseDC(hwndMain, hdc);
\r
9056 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
9061 LoadGameTimerRunning()
\r
9063 return loadGameTimerEvent != 0;
\r
9067 StopLoadGameTimer()
\r
9069 if (loadGameTimerEvent == 0) return FALSE;
\r
9070 KillTimer(hwndMain, loadGameTimerEvent);
\r
9071 loadGameTimerEvent = 0;
\r
9076 StartLoadGameTimer(long millisec)
\r
9078 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
9079 (UINT) millisec, NULL);
\r
9087 char fileTitle[MSG_SIZ];
\r
9089 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
9090 f = OpenFileDialog(hwndMain, "a", defName,
\r
9091 appData.oldSaveStyle ? "gam" : "pgn",
\r
9093 _("Save Game to File"), NULL, fileTitle, NULL);
\r
9095 SaveGame(f, 0, "");
\r
9102 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
9104 if (delayedTimerEvent != 0) {
\r
9105 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
9106 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
9108 KillTimer(hwndMain, delayedTimerEvent);
\r
9109 delayedTimerEvent = 0;
\r
9110 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
9111 delayedTimerCallback();
\r
9113 delayedTimerCallback = cb;
\r
9114 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
9115 (UINT) millisec, NULL);
\r
9118 DelayedEventCallback
\r
9121 if (delayedTimerEvent) {
\r
9122 return delayedTimerCallback;
\r
9129 CancelDelayedEvent()
\r
9131 if (delayedTimerEvent) {
\r
9132 KillTimer(hwndMain, delayedTimerEvent);
\r
9133 delayedTimerEvent = 0;
\r
9137 DWORD GetWin32Priority(int nice)
\r
9138 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
9140 REALTIME_PRIORITY_CLASS 0x00000100
\r
9141 HIGH_PRIORITY_CLASS 0x00000080
\r
9142 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
9143 NORMAL_PRIORITY_CLASS 0x00000020
\r
9144 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
9145 IDLE_PRIORITY_CLASS 0x00000040
\r
9147 if (nice < -15) return 0x00000080;
\r
9148 if (nice < 0) return 0x00008000;
\r
9150 if (nice == 0) return 0x00000020;
\r
9151 if (nice < 15) return 0x00004000;
\r
9152 return 0x00000040;
\r
9155 void RunCommand(char *cmdLine)
\r
9157 /* Now create the child process. */
\r
9158 STARTUPINFO siStartInfo;
\r
9159 PROCESS_INFORMATION piProcInfo;
\r
9161 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9162 siStartInfo.lpReserved = NULL;
\r
9163 siStartInfo.lpDesktop = NULL;
\r
9164 siStartInfo.lpTitle = NULL;
\r
9165 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9166 siStartInfo.cbReserved2 = 0;
\r
9167 siStartInfo.lpReserved2 = NULL;
\r
9168 siStartInfo.hStdInput = NULL;
\r
9169 siStartInfo.hStdOutput = NULL;
\r
9170 siStartInfo.hStdError = NULL;
\r
9172 CreateProcess(NULL,
\r
9173 cmdLine, /* command line */
\r
9174 NULL, /* process security attributes */
\r
9175 NULL, /* primary thread security attrs */
\r
9176 TRUE, /* handles are inherited */
\r
9177 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9178 NULL, /* use parent's environment */
\r
9180 &siStartInfo, /* STARTUPINFO pointer */
\r
9181 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9183 CloseHandle(piProcInfo.hThread);
\r
9186 /* Start a child process running the given program.
\r
9187 The process's standard output can be read from "from", and its
\r
9188 standard input can be written to "to".
\r
9189 Exit with fatal error if anything goes wrong.
\r
9190 Returns an opaque pointer that can be used to destroy the process
\r
9194 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
9196 #define BUFSIZE 4096
\r
9198 HANDLE hChildStdinRd, hChildStdinWr,
\r
9199 hChildStdoutRd, hChildStdoutWr;
\r
9200 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
9201 SECURITY_ATTRIBUTES saAttr;
\r
9203 PROCESS_INFORMATION piProcInfo;
\r
9204 STARTUPINFO siStartInfo;
\r
9206 char buf[MSG_SIZ];
\r
9209 if (appData.debugMode) {
\r
9210 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
9215 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
9216 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
9217 saAttr.bInheritHandle = TRUE;
\r
9218 saAttr.lpSecurityDescriptor = NULL;
\r
9221 * The steps for redirecting child's STDOUT:
\r
9222 * 1. Create anonymous pipe to be STDOUT for child.
\r
9223 * 2. Create a noninheritable duplicate of read handle,
\r
9224 * and close the inheritable read handle.
\r
9227 /* Create a pipe for the child's STDOUT. */
\r
9228 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
9229 return GetLastError();
\r
9232 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
9233 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
9234 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
9235 FALSE, /* not inherited */
\r
9236 DUPLICATE_SAME_ACCESS);
\r
9238 return GetLastError();
\r
9240 CloseHandle(hChildStdoutRd);
\r
9243 * The steps for redirecting child's STDIN:
\r
9244 * 1. Create anonymous pipe to be STDIN for child.
\r
9245 * 2. Create a noninheritable duplicate of write handle,
\r
9246 * and close the inheritable write handle.
\r
9249 /* Create a pipe for the child's STDIN. */
\r
9250 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
9251 return GetLastError();
\r
9254 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
9255 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
9256 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
9257 FALSE, /* not inherited */
\r
9258 DUPLICATE_SAME_ACCESS);
\r
9260 return GetLastError();
\r
9262 CloseHandle(hChildStdinWr);
\r
9264 /* Arrange to (1) look in dir for the child .exe file, and
\r
9265 * (2) have dir be the child's working directory. Interpret
\r
9266 * dir relative to the directory WinBoard loaded from. */
\r
9267 GetCurrentDirectory(MSG_SIZ, buf);
\r
9268 SetCurrentDirectory(installDir);
\r
9269 SetCurrentDirectory(dir);
\r
9271 /* Now create the child process. */
\r
9273 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9274 siStartInfo.lpReserved = NULL;
\r
9275 siStartInfo.lpDesktop = NULL;
\r
9276 siStartInfo.lpTitle = NULL;
\r
9277 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9278 siStartInfo.cbReserved2 = 0;
\r
9279 siStartInfo.lpReserved2 = NULL;
\r
9280 siStartInfo.hStdInput = hChildStdinRd;
\r
9281 siStartInfo.hStdOutput = hChildStdoutWr;
\r
9282 siStartInfo.hStdError = hChildStdoutWr;
\r
9284 fSuccess = CreateProcess(NULL,
\r
9285 cmdLine, /* command line */
\r
9286 NULL, /* process security attributes */
\r
9287 NULL, /* primary thread security attrs */
\r
9288 TRUE, /* handles are inherited */
\r
9289 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9290 NULL, /* use parent's environment */
\r
9292 &siStartInfo, /* STARTUPINFO pointer */
\r
9293 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9295 err = GetLastError();
\r
9296 SetCurrentDirectory(buf); /* return to prev directory */
\r
9301 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
9302 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
9303 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
9306 /* Close the handles we don't need in the parent */
\r
9307 CloseHandle(piProcInfo.hThread);
\r
9308 CloseHandle(hChildStdinRd);
\r
9309 CloseHandle(hChildStdoutWr);
\r
9311 /* Prepare return value */
\r
9312 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9313 cp->kind = CPReal;
\r
9314 cp->hProcess = piProcInfo.hProcess;
\r
9315 cp->pid = piProcInfo.dwProcessId;
\r
9316 cp->hFrom = hChildStdoutRdDup;
\r
9317 cp->hTo = hChildStdinWrDup;
\r
9319 *pr = (void *) cp;
\r
9321 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
9322 2000 where engines sometimes don't see the initial command(s)
\r
9323 from WinBoard and hang. I don't understand how that can happen,
\r
9324 but the Sleep is harmless, so I've put it in. Others have also
\r
9325 reported what may be the same problem, so hopefully this will fix
\r
9326 it for them too. */
\r
9334 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
9336 ChildProc *cp; int result;
\r
9338 cp = (ChildProc *) pr;
\r
9339 if (cp == NULL) return;
\r
9341 switch (cp->kind) {
\r
9343 /* TerminateProcess is considered harmful, so... */
\r
9344 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9345 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9346 /* The following doesn't work because the chess program
\r
9347 doesn't "have the same console" as WinBoard. Maybe
\r
9348 we could arrange for this even though neither WinBoard
\r
9349 nor the chess program uses a console for stdio? */
\r
9350 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9352 /* [AS] Special termination modes for misbehaving programs... */
\r
9353 if( signal & 8 ) {
\r
9354 result = TerminateProcess( cp->hProcess, 0 );
\r
9356 if ( appData.debugMode) {
\r
9357 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9360 else if( signal & 4 ) {
\r
9361 DWORD dw = WaitForSingleObject( cp->hProcess, appData.delayAfterQuit*1000 + 50 ); // Wait 3 seconds at most
\r
9363 if( dw != WAIT_OBJECT_0 ) {
\r
9364 result = TerminateProcess( cp->hProcess, 0 );
\r
9366 if ( appData.debugMode) {
\r
9367 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9373 CloseHandle(cp->hProcess);
\r
9377 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9381 closesocket(cp->sock);
\r
9386 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9387 closesocket(cp->sock);
\r
9388 closesocket(cp->sock2);
\r
9396 InterruptChildProcess(ProcRef pr)
\r
9400 cp = (ChildProc *) pr;
\r
9401 if (cp == NULL) return;
\r
9402 switch (cp->kind) {
\r
9404 /* The following doesn't work because the chess program
\r
9405 doesn't "have the same console" as WinBoard. Maybe
\r
9406 we could arrange for this even though neither WinBoard
\r
9407 nor the chess program uses a console for stdio */
\r
9408 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9413 /* Can't interrupt */
\r
9417 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9424 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9426 char cmdLine[MSG_SIZ];
\r
9428 if (port[0] == NULLCHAR) {
\r
9429 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9431 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9433 return StartChildProcess(cmdLine, "", pr);
\r
9437 /* Code to open TCP sockets */
\r
9440 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9446 struct sockaddr_in sa, mysa;
\r
9447 struct hostent FAR *hp;
\r
9448 unsigned short uport;
\r
9449 WORD wVersionRequested;
\r
9452 /* Initialize socket DLL */
\r
9453 wVersionRequested = MAKEWORD(1, 1);
\r
9454 err = WSAStartup(wVersionRequested, &wsaData);
\r
9455 if (err != 0) return err;
\r
9458 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9459 err = WSAGetLastError();
\r
9464 /* Bind local address using (mostly) don't-care values.
\r
9466 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9467 mysa.sin_family = AF_INET;
\r
9468 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9469 uport = (unsigned short) 0;
\r
9470 mysa.sin_port = htons(uport);
\r
9471 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9472 == SOCKET_ERROR) {
\r
9473 err = WSAGetLastError();
\r
9478 /* Resolve remote host name */
\r
9479 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9480 if (!(hp = gethostbyname(host))) {
\r
9481 unsigned int b0, b1, b2, b3;
\r
9483 err = WSAGetLastError();
\r
9485 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9486 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9487 hp->h_addrtype = AF_INET;
\r
9489 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9490 hp->h_addr_list[0] = (char *) malloc(4);
\r
9491 hp->h_addr_list[0][0] = (char) b0;
\r
9492 hp->h_addr_list[0][1] = (char) b1;
\r
9493 hp->h_addr_list[0][2] = (char) b2;
\r
9494 hp->h_addr_list[0][3] = (char) b3;
\r
9500 sa.sin_family = hp->h_addrtype;
\r
9501 uport = (unsigned short) atoi(port);
\r
9502 sa.sin_port = htons(uport);
\r
9503 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9505 /* Make connection */
\r
9506 if (connect(s, (struct sockaddr *) &sa,
\r
9507 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9508 err = WSAGetLastError();
\r
9513 /* Prepare return value */
\r
9514 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9515 cp->kind = CPSock;
\r
9517 *pr = (ProcRef *) cp;
\r
9523 OpenCommPort(char *name, ProcRef *pr)
\r
9528 char fullname[MSG_SIZ];
\r
9530 if (*name != '\\')
\r
9531 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9533 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9535 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9536 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9537 if (h == (HANDLE) -1) {
\r
9538 return GetLastError();
\r
9542 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9544 /* Accumulate characters until a 100ms pause, then parse */
\r
9545 ct.ReadIntervalTimeout = 100;
\r
9546 ct.ReadTotalTimeoutMultiplier = 0;
\r
9547 ct.ReadTotalTimeoutConstant = 0;
\r
9548 ct.WriteTotalTimeoutMultiplier = 0;
\r
9549 ct.WriteTotalTimeoutConstant = 0;
\r
9550 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9552 /* Prepare return value */
\r
9553 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9554 cp->kind = CPComm;
\r
9557 *pr = (ProcRef *) cp;
\r
9563 OpenLoopback(ProcRef *pr)
\r
9565 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9571 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9576 struct sockaddr_in sa, mysa;
\r
9577 struct hostent FAR *hp;
\r
9578 unsigned short uport;
\r
9579 WORD wVersionRequested;
\r
9582 char stderrPortStr[MSG_SIZ];
\r
9584 /* Initialize socket DLL */
\r
9585 wVersionRequested = MAKEWORD(1, 1);
\r
9586 err = WSAStartup(wVersionRequested, &wsaData);
\r
9587 if (err != 0) return err;
\r
9589 /* Resolve remote host name */
\r
9590 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9591 if (!(hp = gethostbyname(host))) {
\r
9592 unsigned int b0, b1, b2, b3;
\r
9594 err = WSAGetLastError();
\r
9596 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9597 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9598 hp->h_addrtype = AF_INET;
\r
9600 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9601 hp->h_addr_list[0] = (char *) malloc(4);
\r
9602 hp->h_addr_list[0][0] = (char) b0;
\r
9603 hp->h_addr_list[0][1] = (char) b1;
\r
9604 hp->h_addr_list[0][2] = (char) b2;
\r
9605 hp->h_addr_list[0][3] = (char) b3;
\r
9611 sa.sin_family = hp->h_addrtype;
\r
9612 uport = (unsigned short) 514;
\r
9613 sa.sin_port = htons(uport);
\r
9614 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9616 /* Bind local socket to unused "privileged" port address
\r
9618 s = INVALID_SOCKET;
\r
9619 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9620 mysa.sin_family = AF_INET;
\r
9621 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9622 for (fromPort = 1023;; fromPort--) {
\r
9623 if (fromPort < 0) {
\r
9625 return WSAEADDRINUSE;
\r
9627 if (s == INVALID_SOCKET) {
\r
9628 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9629 err = WSAGetLastError();
\r
9634 uport = (unsigned short) fromPort;
\r
9635 mysa.sin_port = htons(uport);
\r
9636 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9637 == SOCKET_ERROR) {
\r
9638 err = WSAGetLastError();
\r
9639 if (err == WSAEADDRINUSE) continue;
\r
9643 if (connect(s, (struct sockaddr *) &sa,
\r
9644 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9645 err = WSAGetLastError();
\r
9646 if (err == WSAEADDRINUSE) {
\r
9657 /* Bind stderr local socket to unused "privileged" port address
\r
9659 s2 = INVALID_SOCKET;
\r
9660 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9661 mysa.sin_family = AF_INET;
\r
9662 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9663 for (fromPort = 1023;; fromPort--) {
\r
9664 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9665 if (fromPort < 0) {
\r
9666 (void) closesocket(s);
\r
9668 return WSAEADDRINUSE;
\r
9670 if (s2 == INVALID_SOCKET) {
\r
9671 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9672 err = WSAGetLastError();
\r
9678 uport = (unsigned short) fromPort;
\r
9679 mysa.sin_port = htons(uport);
\r
9680 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9681 == SOCKET_ERROR) {
\r
9682 err = WSAGetLastError();
\r
9683 if (err == WSAEADDRINUSE) continue;
\r
9684 (void) closesocket(s);
\r
9688 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9689 err = WSAGetLastError();
\r
9690 if (err == WSAEADDRINUSE) {
\r
9692 s2 = INVALID_SOCKET;
\r
9695 (void) closesocket(s);
\r
9696 (void) closesocket(s2);
\r
9702 prevStderrPort = fromPort; // remember port used
\r
9703 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9705 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9706 err = WSAGetLastError();
\r
9707 (void) closesocket(s);
\r
9708 (void) closesocket(s2);
\r
9713 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9714 err = WSAGetLastError();
\r
9715 (void) closesocket(s);
\r
9716 (void) closesocket(s2);
\r
9720 if (*user == NULLCHAR) user = UserName();
\r
9721 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9722 err = WSAGetLastError();
\r
9723 (void) closesocket(s);
\r
9724 (void) closesocket(s2);
\r
9728 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9729 err = WSAGetLastError();
\r
9730 (void) closesocket(s);
\r
9731 (void) closesocket(s2);
\r
9736 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9737 err = WSAGetLastError();
\r
9738 (void) closesocket(s);
\r
9739 (void) closesocket(s2);
\r
9743 (void) closesocket(s2); /* Stop listening */
\r
9745 /* Prepare return value */
\r
9746 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9747 cp->kind = CPRcmd;
\r
9750 *pr = (ProcRef *) cp;
\r
9757 AddInputSource(ProcRef pr, int lineByLine,
\r
9758 InputCallback func, VOIDSTAR closure)
\r
9760 InputSource *is, *is2 = NULL;
\r
9761 ChildProc *cp = (ChildProc *) pr;
\r
9763 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9764 is->lineByLine = lineByLine;
\r
9766 is->closure = closure;
\r
9767 is->second = NULL;
\r
9768 is->next = is->buf;
\r
9769 if (pr == NoProc) {
\r
9770 is->kind = CPReal;
\r
9771 consoleInputSource = is;
\r
9773 is->kind = cp->kind;
\r
9775 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9776 we create all threads suspended so that the is->hThread variable can be
\r
9777 safely assigned, then let the threads start with ResumeThread.
\r
9779 switch (cp->kind) {
\r
9781 is->hFile = cp->hFrom;
\r
9782 cp->hFrom = NULL; /* now owned by InputThread */
\r
9784 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9785 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9789 is->hFile = cp->hFrom;
\r
9790 cp->hFrom = NULL; /* now owned by InputThread */
\r
9792 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9793 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9797 is->sock = cp->sock;
\r
9799 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9800 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9804 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9806 is->sock = cp->sock;
\r
9808 is2->sock = cp->sock2;
\r
9809 is2->second = is2;
\r
9811 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9812 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9814 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9815 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9819 if( is->hThread != NULL ) {
\r
9820 ResumeThread( is->hThread );
\r
9823 if( is2 != NULL && is2->hThread != NULL ) {
\r
9824 ResumeThread( is2->hThread );
\r
9828 return (InputSourceRef) is;
\r
9832 RemoveInputSource(InputSourceRef isr)
\r
9836 is = (InputSource *) isr;
\r
9837 is->hThread = NULL; /* tell thread to stop */
\r
9838 CloseHandle(is->hThread);
\r
9839 if (is->second != NULL) {
\r
9840 is->second->hThread = NULL;
\r
9841 CloseHandle(is->second->hThread);
\r
9845 int no_wrap(char *message, int count)
\r
9847 ConsoleOutput(message, count, FALSE);
\r
9852 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9855 int outCount = SOCKET_ERROR;
\r
9856 ChildProc *cp = (ChildProc *) pr;
\r
9857 static OVERLAPPED ovl;
\r
9859 static int line = 0;
\r
9863 if (appData.noJoin || !appData.useInternalWrap)
\r
9864 return no_wrap(message, count);
\r
9867 int width = get_term_width();
\r
9868 int len = wrap(NULL, message, count, width, &line);
\r
9869 char *msg = malloc(len);
\r
9873 return no_wrap(message, count);
\r
9876 dbgchk = wrap(msg, message, count, width, &line);
\r
9877 if (dbgchk != len && appData.debugMode)
\r
9878 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9879 ConsoleOutput(msg, len, FALSE);
\r
9886 if (ovl.hEvent == NULL) {
\r
9887 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9889 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9891 switch (cp->kind) {
\r
9894 outCount = send(cp->sock, message, count, 0);
\r
9895 if (outCount == SOCKET_ERROR) {
\r
9896 *outError = WSAGetLastError();
\r
9898 *outError = NO_ERROR;
\r
9903 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9904 &dOutCount, NULL)) {
\r
9905 *outError = NO_ERROR;
\r
9906 outCount = (int) dOutCount;
\r
9908 *outError = GetLastError();
\r
9913 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9914 &dOutCount, &ovl);
\r
9915 if (*outError == NO_ERROR) {
\r
9916 outCount = (int) dOutCount;
\r
9926 if(n != 0) Sleep(n);
\r
9930 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9933 /* Ignore delay, not implemented for WinBoard */
\r
9934 return OutputToProcess(pr, message, count, outError);
\r
9939 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9940 char *buf, int count, int error)
\r
9942 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9945 /* see wgamelist.c for Game List functions */
\r
9946 /* see wedittags.c for Edit Tags functions */
\r
9953 char buf[MSG_SIZ];
\r
9956 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9957 f = fopen(buf, "r");
\r
9959 ProcessICSInitScript(f);
\r
9969 StartAnalysisClock()
\r
9971 if (analysisTimerEvent) return;
\r
9972 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9973 (UINT) 2000, NULL);
\r
9977 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9979 highlightInfo.sq[0].x = fromX;
\r
9980 highlightInfo.sq[0].y = fromY;
\r
9981 highlightInfo.sq[1].x = toX;
\r
9982 highlightInfo.sq[1].y = toY;
\r
9988 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9989 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9993 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9995 premoveHighlightInfo.sq[0].x = fromX;
\r
9996 premoveHighlightInfo.sq[0].y = fromY;
\r
9997 premoveHighlightInfo.sq[1].x = toX;
\r
9998 premoveHighlightInfo.sq[1].y = toY;
\r
10002 ClearPremoveHighlights()
\r
10004 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
10005 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
10009 ShutDownFrontEnd()
\r
10011 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
10012 DeleteClipboardTempFiles();
\r
10018 if (IsIconic(hwndMain))
\r
10019 ShowWindow(hwndMain, SW_RESTORE);
\r
10021 SetActiveWindow(hwndMain);
\r
10025 * Prototypes for animation support routines
\r
10027 static void ScreenSquare(int column, int row, POINT * pt);
\r
10028 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
10029 POINT frames[], int * nFrames);
\r
10032 #define kFactor 4
\r
10035 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
10036 { // [HGM] atomic: animate blast wave
\r
10039 explodeInfo.fromX = fromX;
\r
10040 explodeInfo.fromY = fromY;
\r
10041 explodeInfo.toX = toX;
\r
10042 explodeInfo.toY = toY;
\r
10043 for(i=1; i<4*kFactor; i++) {
\r
10044 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
10045 DrawPosition(FALSE, board);
\r
10046 Sleep(appData.animSpeed);
\r
10048 explodeInfo.radius = 0;
\r
10049 DrawPosition(TRUE, board);
\r
10053 AnimateMove(board, fromX, fromY, toX, toY)
\r
10060 ChessSquare piece, victim = EmptySquare, victim2 = EmptySquare;
\r
10061 int x = toX, y = toY, x2 = kill2X;
\r
10062 POINT start, finish, mid;
\r
10063 POINT frames[kFactor * 2 + 1];
\r
10066 if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();
\r
10068 if (!appData.animate) return;
\r
10069 if (doingSizing) return;
\r
10070 if (fromY < 0 || fromX < 0) return;
\r
10071 piece = board[fromY][fromX];
\r
10072 if (piece >= EmptySquare) return;
\r
10074 if(x2 >= 0) toX = kill2X, toY = kill2Y, victim = board[killY][killX], victim2 = board[kill2Y][kill2X]; else
\r
10075 if(killX >= 0) toX = killX, toY = killY, victim = board[killY][killX]; // [HGM] lion: first to kill square
\r
10077 animInfo.from.x = fromX;
\r
10078 animInfo.from.y = fromY;
\r
10082 ScreenSquare(fromX, fromY, &start);
\r
10083 ScreenSquare(toX, toY, &finish);
\r
10085 /* All moves except knight jumps move in straight line */
\r
10086 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
10087 mid.x = start.x + (finish.x - start.x) / 2;
\r
10088 mid.y = start.y + (finish.y - start.y) / 2;
\r
10090 /* Knight: make straight movement then diagonal */
\r
10091 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
10092 mid.x = start.x + (finish.x - start.x) / 2;
\r
10096 mid.y = start.y + (finish.y - start.y) / 2;
\r
10100 /* Don't use as many frames for very short moves */
\r
10101 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
10102 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
10104 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
10106 animInfo.to.x = toX;
\r
10107 animInfo.to.y = toY;
\r
10108 animInfo.lastpos = start;
\r
10109 animInfo.piece = piece;
\r
10110 for (n = 0; n < nFrames; n++) {
\r
10111 animInfo.pos = frames[n];
\r
10112 DrawPosition(FALSE, board);
\r
10113 animInfo.lastpos = animInfo.pos;
\r
10114 Sleep(appData.animSpeed);
\r
10116 animInfo.pos = finish;
\r
10117 DrawPosition(FALSE, board);
\r
10119 if(toX == x2 && toY == kill2Y) {
\r
10120 fromX = toX; fromY = toY; toX = killX; toY = killY; x2 = -1;
\r
10121 board[kill2Y][kill2X] = EmptySquare; goto again;
\r
10123 if(toX != x || toY != y) {
\r
10124 fromX = toX; fromY = toY; toX = x; toY = y;
\r
10125 board[killY][killX] = EmptySquare; goto again;
\r
10128 if(victim2 != EmptySquare) board[kill2Y][kill2X] = victim2;
\r
10129 if(victim != EmptySquare) board[killY][killX] = victim;
\r
10131 animInfo.piece = EmptySquare;
\r
10132 Explode(board, fromX, fromY, toX, toY);
\r
10135 /* Convert board position to corner of screen rect and color */
\r
10138 ScreenSquare(column, row, pt)
\r
10139 int column; int row; POINT * pt;
\r
10142 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
10143 pt->y = lineGap + row * (squareSize + lineGap) + border;
\r
10145 pt->x = lineGap + column * (squareSize + lineGap) + border;
\r
10146 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
10150 /* Generate a series of frame coords from start->mid->finish.
\r
10151 The movement rate doubles until the half way point is
\r
10152 reached, then halves back down to the final destination,
\r
10153 which gives a nice slow in/out effect. The algorithmn
\r
10154 may seem to generate too many intermediates for short
\r
10155 moves, but remember that the purpose is to attract the
\r
10156 viewers attention to the piece about to be moved and
\r
10157 then to where it ends up. Too few frames would be less
\r
10161 Tween(start, mid, finish, factor, frames, nFrames)
\r
10162 POINT * start; POINT * mid;
\r
10163 POINT * finish; int factor;
\r
10164 POINT frames[]; int * nFrames;
\r
10166 int n, fraction = 1, count = 0;
\r
10168 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
10169 for (n = 0; n < factor; n++)
\r
10171 for (n = 0; n < factor; n++) {
\r
10172 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
10173 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
10175 fraction = fraction / 2;
\r
10179 frames[count] = *mid;
\r
10182 /* Slow out, stepping 1/2, then 1/4, ... */
\r
10184 for (n = 0; n < factor; n++) {
\r
10185 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
10186 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
10188 fraction = fraction * 2;
\r
10190 *nFrames = count;
\r
10194 SettingsPopUp(ChessProgramState *cps)
\r
10195 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
10196 EngineOptionsPopup(savedHwnd, cps);
\r
10199 int flock(int fid, int code)
\r
10201 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
10203 ov.hEvent = NULL;
\r
10205 ov.OffsetHigh = 0;
\r
10207 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
10209 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
10210 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
10211 default: return -1;
\r
10220 static char col[8][20];
\r
10221 COLORREF color = *(COLORREF *) colorVariable[n];
\r
10223 snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
10228 ActivateTheme (int new)
\r
10229 { // Redo initialization of features depending on options that can occur in themes
\r
10231 if(new) InitDrawingColors();
\r
10232 fontBitmapSquareSize = 0; // request creation of new font pieces
\r
10233 InitDrawingSizes(boardSize, 0);
\r
10234 InvalidateRect(hwndMain, NULL, TRUE);
\r