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
3757 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3759 static Board lastReq[2], lastDrawn[2];
\r
3760 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3761 static int lastDrawnFlipView = 0;
\r
3762 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3763 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3766 HBITMAP bufferBitmap;
\r
3767 HBITMAP oldBitmap;
\r
3769 HRGN clips[MAX_CLIPS];
\r
3770 ChessSquare dragged_piece = EmptySquare;
\r
3771 int nr = twoBoards*partnerUp;
\r
3773 /* I'm undecided on this - this function figures out whether a full
\r
3774 * repaint is necessary on its own, so there's no real reason to have the
\r
3775 * caller tell it that. I think this can safely be set to FALSE - but
\r
3776 * if we trust the callers not to request full repaints unnessesarily, then
\r
3777 * we could skip some clipping work. In other words, only request a full
\r
3778 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3779 * gamestart and similar) --Hawk
\r
3781 Boolean fullrepaint = repaint;
\r
3783 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3785 if( DrawPositionNeedsFullRepaint() ) {
\r
3786 fullrepaint = TRUE;
\r
3789 if (board == NULL) {
\r
3790 if (!lastReqValid[nr]) {
\r
3793 board = lastReq[nr];
\r
3795 CopyBoard(lastReq[nr], board);
\r
3796 lastReqValid[nr] = 1;
\r
3799 if (doingSizing) {
\r
3803 if (IsIconic(hwndMain)) {
\r
3807 if (hdc == NULL) {
\r
3808 hdc = GetDC(hwndMain);
\r
3809 if (!appData.monoMode) {
\r
3810 SelectPalette(hdc, hPal, FALSE);
\r
3811 RealizePalette(hdc);
\r
3815 releaseDC = FALSE;
\r
3818 /* Create some work-DCs */
\r
3819 hdcmem = CreateCompatibleDC(hdc);
\r
3820 tmphdc = CreateCompatibleDC(hdc);
\r
3822 /* If dragging is in progress, we temporarely remove the piece */
\r
3823 /* [HGM] or temporarily decrease count if stacked */
\r
3824 /* !! Moved to before board compare !! */
\r
3825 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3826 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3827 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3828 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3829 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3831 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3832 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3833 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3835 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3838 /* Figure out which squares need updating by comparing the
\r
3839 * newest board with the last drawn board and checking if
\r
3840 * flipping has changed.
\r
3842 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3843 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3844 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3845 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3846 SquareToPos(row, column, &x, &y);
\r
3847 clips[num_clips++] =
\r
3848 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3852 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3853 for (i=0; i<2; i++) {
\r
3854 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3855 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3856 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3857 lastDrawnHighlight.sq[i].y >= 0) {
\r
3858 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3859 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3860 clips[num_clips++] =
\r
3861 CreateRectRgn(x - lineGap, y - lineGap,
\r
3862 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3864 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3865 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3866 clips[num_clips++] =
\r
3867 CreateRectRgn(x - lineGap, y - lineGap,
\r
3868 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3872 for (i=0; i<2; i++) {
\r
3873 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3874 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3875 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3876 lastDrawnPremove.sq[i].y >= 0) {
\r
3877 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3878 lastDrawnPremove.sq[i].x, &x, &y);
\r
3879 clips[num_clips++] =
\r
3880 CreateRectRgn(x - lineGap, y - lineGap,
\r
3881 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3883 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3884 premoveHighlightInfo.sq[i].y >= 0) {
\r
3885 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3886 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3887 clips[num_clips++] =
\r
3888 CreateRectRgn(x - lineGap, y - lineGap,
\r
3889 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3893 } else { // nr == 1
\r
3894 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3895 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3896 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3897 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3898 for (i=0; i<2; i++) {
\r
3899 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3900 partnerHighlightInfo.sq[i].y >= 0) {
\r
3901 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3902 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3903 clips[num_clips++] =
\r
3904 CreateRectRgn(x - lineGap, y - lineGap,
\r
3905 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3907 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3908 oldPartnerHighlight.sq[i].y >= 0) {
\r
3909 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3910 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3911 clips[num_clips++] =
\r
3912 CreateRectRgn(x - lineGap, y - lineGap,
\r
3913 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3918 fullrepaint = TRUE;
\r
3921 /* Create a buffer bitmap - this is the actual bitmap
\r
3922 * being written to. When all the work is done, we can
\r
3923 * copy it to the real DC (the screen). This avoids
\r
3924 * the problems with flickering.
\r
3926 GetClientRect(hwndMain, &Rect);
\r
3927 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3928 Rect.bottom-Rect.top+1);
\r
3929 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3930 if (!appData.monoMode) {
\r
3931 SelectPalette(hdcmem, hPal, FALSE);
\r
3934 /* Create clips for dragging */
\r
3935 if (!fullrepaint) {
\r
3936 if (dragInfo.from.x >= 0) {
\r
3937 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3938 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3940 if (dragInfo.start.x >= 0) {
\r
3941 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3942 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3944 if (dragInfo.pos.x >= 0) {
\r
3945 x = dragInfo.pos.x - squareSize / 2;
\r
3946 y = dragInfo.pos.y - squareSize / 2;
\r
3947 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3949 if (dragInfo.lastpos.x >= 0) {
\r
3950 x = dragInfo.lastpos.x - squareSize / 2;
\r
3951 y = dragInfo.lastpos.y - squareSize / 2;
\r
3952 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3956 /* Are we animating a move?
\r
3958 * - remove the piece from the board (temporarely)
\r
3959 * - calculate the clipping region
\r
3961 if (!fullrepaint) {
\r
3962 if (animInfo.piece != EmptySquare) {
\r
3963 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3964 x = boardRect.left + animInfo.lastpos.x;
\r
3965 y = boardRect.top + animInfo.lastpos.y;
\r
3966 x2 = boardRect.left + animInfo.pos.x;
\r
3967 y2 = boardRect.top + animInfo.pos.y;
\r
3968 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3969 /* Slight kludge. The real problem is that after AnimateMove is
\r
3970 done, the position on the screen does not match lastDrawn.
\r
3971 This currently causes trouble only on e.p. captures in
\r
3972 atomic, where the piece moves to an empty square and then
\r
3973 explodes. The old and new positions both had an empty square
\r
3974 at the destination, but animation has drawn a piece there and
\r
3975 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3976 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3980 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3981 if (num_clips == 0)
\r
3982 fullrepaint = TRUE;
\r
3984 /* Set clipping on the memory DC */
\r
3985 if (!fullrepaint) {
\r
3986 SelectClipRgn(hdcmem, clips[0]);
\r
3987 for (x = 1; x < num_clips; x++) {
\r
3988 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3989 abort(); // this should never ever happen!
\r
3993 /* Do all the drawing to the memory DC */
\r
3994 if(explodeInfo.radius) { // [HGM] atomic
\r
3996 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3997 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3998 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3999 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
4000 x += squareSize/2;
\r
4001 y += squareSize/2;
\r
4002 if(!fullrepaint) {
\r
4003 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
4004 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
4006 DrawGridOnDC(hdcmem);
\r
4007 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
4008 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
4009 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
4010 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
4011 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
4012 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
4013 SelectObject(hdcmem, oldBrush);
\r
4015 if(border) DrawBackgroundOnDC(hdcmem);
\r
4016 DrawGridOnDC(hdcmem);
\r
4017 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
4018 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
4019 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
4021 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
4022 oldPartnerHighlight = partnerHighlightInfo;
\r
4024 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
4026 if(nr == 0) // [HGM] dual: markers only on left board
\r
4027 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
4028 for (column = 0; column < BOARD_WIDTH; column++) {
\r
4029 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
4030 HBRUSH oldBrush = SelectObject(hdcmem, markerBrush[marker[row][column]-1]);
\r
4031 SquareToPos(row, column, &x, &y);
\r
4032 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
4033 x + 3*squareSize/4, y + 3*squareSize/4);
\r
4034 SelectObject(hdcmem, oldBrush);
\r
4039 if( appData.highlightMoveWithArrow ) {
\r
4041 DrawArrowHighlight(hdcmem);
\r
4044 DrawCoordsOnDC(hdcmem);
\r
4046 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
4047 /* to make sure lastDrawn contains what is actually drawn */
\r
4049 /* Put the dragged piece back into place and draw it (out of place!) */
\r
4050 if (dragged_piece != EmptySquare) {
\r
4051 /* [HGM] or restack */
\r
4052 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
4053 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
4055 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
4056 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
4058 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
4059 x = dragInfo.pos.x - squareSize / 2;
\r
4060 y = dragInfo.pos.y - squareSize / 2;
\r
4061 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
4062 ((int) dragInfo.piece < (int) BlackPawn),
\r
4063 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
4066 /* Put the animated piece back into place and draw it */
\r
4067 if (animInfo.piece != EmptySquare) {
\r
4068 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
4069 x = boardRect.left + animInfo.pos.x;
\r
4070 y = boardRect.top + animInfo.pos.y;
\r
4071 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
4072 ((int) animInfo.piece < (int) BlackPawn),
\r
4073 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
4076 /* Release the bufferBitmap by selecting in the old bitmap
\r
4077 * and delete the memory DC
\r
4079 SelectObject(hdcmem, oldBitmap);
\r
4082 /* Set clipping on the target DC */
\r
4083 if (!fullrepaint) {
\r
4084 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
4086 GetRgnBox(clips[x], &rect);
\r
4087 DeleteObject(clips[x]);
\r
4088 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
4089 rect.right + wpMain.width/2, rect.bottom);
\r
4091 SelectClipRgn(hdc, clips[0]);
\r
4092 for (x = 1; x < num_clips; x++) {
\r
4093 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
4094 abort(); // this should never ever happen!
\r
4098 /* Copy the new bitmap onto the screen in one go.
\r
4099 * This way we avoid any flickering
\r
4101 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
4102 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
4103 boardRect.right - boardRect.left,
\r
4104 boardRect.bottom - boardRect.top,
\r
4105 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
4106 if(saveDiagFlag) {
\r
4107 BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData;
\r
4108 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
4110 GetObject(bufferBitmap, sizeof(b), &b);
\r
4111 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\r
4112 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
4113 bih.biWidth = b.bmWidth;
\r
4114 bih.biHeight = b.bmHeight;
\r
4116 bih.biBitCount = b.bmBitsPixel;
\r
4117 bih.biCompression = 0;
\r
4118 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
4119 bih.biXPelsPerMeter = 0;
\r
4120 bih.biYPelsPerMeter = 0;
\r
4121 bih.biClrUsed = 0;
\r
4122 bih.biClrImportant = 0;
\r
4123 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
4124 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
4125 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
4126 // fprintf(diagFile, "%8x\n", (int) pData);
\r
4128 wb = b.bmWidthBytes;
\r
4130 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
4131 int k = ((int*) pData)[i];
\r
4132 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4133 if(j >= 16) break;
\r
4135 if(j >= nrColors) nrColors = j+1;
\r
4137 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
4139 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
4140 for(w=0; w<(wb>>2); w+=2) {
\r
4141 int k = ((int*) pData)[(wb*i>>2) + w];
\r
4142 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4143 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
4144 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
4145 pData[p++] = m | j<<4;
\r
4147 while(p&3) pData[p++] = 0;
\r
4150 wb = ((wb+31)>>5)<<2;
\r
4152 // write BITMAPFILEHEADER
\r
4153 fprintf(diagFile, "BM");
\r
4154 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
4155 fputDW(diagFile, 0);
\r
4156 fputDW(diagFile, 0x36 + (fac?64:0));
\r
4157 // write BITMAPINFOHEADER
\r
4158 fputDW(diagFile, 40);
\r
4159 fputDW(diagFile, b.bmWidth);
\r
4160 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
4161 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
4162 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
4163 fputDW(diagFile, 0);
\r
4164 fputDW(diagFile, 0);
\r
4165 fputDW(diagFile, 0);
\r
4166 fputDW(diagFile, 0);
\r
4167 fputDW(diagFile, 0);
\r
4168 fputDW(diagFile, 0);
\r
4169 // write color table
\r
4171 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
4172 // write bitmap data
\r
4173 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
4174 fputc(pData[i], diagFile);
\r
4179 SelectObject(tmphdc, oldBitmap);
\r
4181 /* Massive cleanup */
\r
4182 for (x = 0; x < num_clips; x++)
\r
4183 DeleteObject(clips[x]);
\r
4186 DeleteObject(bufferBitmap);
\r
4189 ReleaseDC(hwndMain, hdc);
\r
4191 if (lastDrawnFlipView != flipView && nr == 0) {
\r
4193 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
4195 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
4198 /* CopyBoard(lastDrawn, board);*/
\r
4199 lastDrawnHighlight = highlightInfo;
\r
4200 lastDrawnPremove = premoveHighlightInfo;
\r
4201 lastDrawnFlipView = flipView;
\r
4202 lastDrawnValid[nr] = 1;
\r
4205 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
4210 saveDiagFlag = 1; diagFile = f;
\r
4211 HDCDrawPosition(NULL, TRUE, NULL);
\r
4219 /*---------------------------------------------------------------------------*\
\r
4220 | CLIENT PAINT PROCEDURE
\r
4221 | This is the main event-handler for the WM_PAINT message.
\r
4223 \*---------------------------------------------------------------------------*/
\r
4225 PaintProc(HWND hwnd)
\r
4231 if((hdc = BeginPaint(hwnd, &ps))) {
\r
4232 if (IsIconic(hwnd)) {
\r
4233 DrawIcon(hdc, 2, 2, iconCurrent);
\r
4235 if (!appData.monoMode) {
\r
4236 SelectPalette(hdc, hPal, FALSE);
\r
4237 RealizePalette(hdc);
\r
4239 HDCDrawPosition(hdc, 1, NULL);
\r
4240 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
4241 flipView = !flipView; partnerUp = !partnerUp;
\r
4242 HDCDrawPosition(hdc, 1, NULL);
\r
4243 flipView = !flipView; partnerUp = !partnerUp;
\r
4246 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
4247 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
4248 ETO_CLIPPED|ETO_OPAQUE,
\r
4249 &messageRect, messageText, strlen(messageText), NULL);
\r
4250 SelectObject(hdc, oldFont);
\r
4251 DisplayBothClocks();
\r
4254 EndPaint(hwnd,&ps);
\r
4262 * If the user selects on a border boundary, return -1; if off the board,
\r
4263 * return -2. Otherwise map the event coordinate to the square.
\r
4264 * The offset boardRect.left or boardRect.top must already have been
\r
4265 * subtracted from x.
\r
4267 int EventToSquare(x, limit)
\r
4272 if (x < lineGap + border)
\r
4274 x -= lineGap + border;
\r
4275 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4277 x /= (squareSize + lineGap);
\r
4289 DropEnable dropEnables[] = {
\r
4290 { 'P', DP_Pawn, N_("Pawn") },
\r
4291 { 'N', DP_Knight, N_("Knight") },
\r
4292 { 'B', DP_Bishop, N_("Bishop") },
\r
4293 { 'R', DP_Rook, N_("Rook") },
\r
4294 { 'Q', DP_Queen, N_("Queen") },
\r
4298 SetupDropMenu(HMENU hmenu)
\r
4300 int i, count, enable;
\r
4302 extern char white_holding[], black_holding[];
\r
4303 char item[MSG_SIZ];
\r
4305 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4306 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4307 dropEnables[i].piece);
\r
4309 while (p && *p++ == dropEnables[i].piece) count++;
\r
4310 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4311 enable = count > 0 || !appData.testLegality
\r
4312 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4313 && !appData.icsActive);
\r
4314 ModifyMenu(hmenu, dropEnables[i].command,
\r
4315 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4316 dropEnables[i].command, item);
\r
4320 void DragPieceBegin(int x, int y, Boolean instantly)
\r
4322 dragInfo.lastpos.x = boardRect.left + x;
\r
4323 dragInfo.lastpos.y = boardRect.top + y;
\r
4324 if(instantly) dragInfo.pos = dragInfo.lastpos;
\r
4325 dragInfo.from.x = fromX;
\r
4326 dragInfo.from.y = fromY;
\r
4327 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4328 dragInfo.start = dragInfo.from;
\r
4329 SetCapture(hwndMain);
\r
4332 void DragPieceEnd(int x, int y)
\r
4335 dragInfo.start.x = dragInfo.start.y = -1;
\r
4336 dragInfo.from = dragInfo.start;
\r
4337 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4340 void ChangeDragPiece(ChessSquare piece)
\r
4342 dragInfo.piece = piece;
\r
4345 /* Event handler for mouse messages */
\r
4347 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4351 static int recursive = 0;
\r
4353 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4356 if (message == WM_MBUTTONUP) {
\r
4357 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4358 to the middle button: we simulate pressing the left button too!
\r
4360 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4361 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4367 pt.x = LOWORD(lParam);
\r
4368 pt.y = HIWORD(lParam);
\r
4369 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4370 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4371 if (!flipView && y >= 0) {
\r
4372 y = BOARD_HEIGHT - 1 - y;
\r
4374 if (flipView && x >= 0) {
\r
4375 x = BOARD_WIDTH - 1 - x;
\r
4378 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4379 controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status
\r
4381 switch (message) {
\r
4382 case WM_LBUTTONDOWN:
\r
4383 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4384 ClockClick(flipClock); break;
\r
4385 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4386 ClockClick(!flipClock); break;
\r
4388 if(dragging) { // [HGM] lion: don't destroy dragging info if we are already dragging
\r
4389 dragInfo.start.x = dragInfo.start.y = -1;
\r
4390 dragInfo.from = dragInfo.start;
\r
4392 if(fromX == -1 && frozen) { // not sure where this is for
\r
4393 fromX = fromY = -1;
\r
4394 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4397 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4398 DrawPosition(TRUE, NULL);
\r
4401 case WM_LBUTTONUP:
\r
4402 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4403 DrawPosition(TRUE, NULL);
\r
4406 case WM_MOUSEMOVE:
\r
4407 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4408 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4409 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4410 if ((appData.animateDragging || appData.highlightDragging)
\r
4411 && (wParam & MK_LBUTTON || dragging == 2)
\r
4412 && dragInfo.from.x >= 0)
\r
4414 BOOL full_repaint = FALSE;
\r
4416 if (appData.animateDragging) {
\r
4417 dragInfo.pos = pt;
\r
4419 if (appData.highlightDragging) {
\r
4420 HoverEvent(highlightInfo.sq[1].x, highlightInfo.sq[1].y, x, y);
\r
4421 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4422 full_repaint = TRUE;
\r
4426 DrawPosition( full_repaint, NULL);
\r
4428 dragInfo.lastpos = dragInfo.pos;
\r
4432 case WM_MOUSEWHEEL: // [DM]
\r
4433 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4434 /* Mouse Wheel is being rolled forward
\r
4435 * Play moves forward
\r
4437 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4438 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4439 /* Mouse Wheel is being rolled backward
\r
4440 * Play moves backward
\r
4442 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4443 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4447 case WM_MBUTTONUP:
\r
4448 case WM_RBUTTONUP:
\r
4450 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4453 case WM_MBUTTONDOWN:
\r
4454 case WM_RBUTTONDOWN:
\r
4457 fromX = fromY = -1;
\r
4458 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4459 dragInfo.start.x = dragInfo.start.y = -1;
\r
4460 dragInfo.from = dragInfo.start;
\r
4461 dragInfo.lastpos = dragInfo.pos;
\r
4462 if (appData.highlightDragging) {
\r
4463 ClearHighlights();
\r
4466 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4467 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4468 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4469 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4470 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4474 DrawPosition(TRUE, NULL);
\r
4476 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4479 if (message == WM_MBUTTONDOWN) {
\r
4480 buttonCount = 3; /* even if system didn't think so */
\r
4481 if (wParam & MK_SHIFT)
\r
4482 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4484 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4485 } else { /* message == WM_RBUTTONDOWN */
\r
4486 /* Just have one menu, on the right button. Windows users don't
\r
4487 think to try the middle one, and sometimes other software steals
\r
4488 it, or it doesn't really exist. */
\r
4489 if(gameInfo.variant != VariantShogi)
\r
4490 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4492 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4496 SetCapture(hwndMain);
\r
4499 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4500 SetupDropMenu(hmenu);
\r
4501 MenuPopup(hwnd, pt, hmenu, -1);
\r
4511 /* Preprocess messages for buttons in main window */
\r
4513 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4515 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4518 for (i=0; i<N_BUTTONS; i++) {
\r
4519 if (buttonDesc[i].id == id) break;
\r
4521 if (i == N_BUTTONS) return 0;
\r
4522 switch (message) {
\r
4527 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4528 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4535 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4538 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4539 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4540 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4541 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4543 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4545 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4546 TypeInEvent((char)wParam);
\r
4552 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4555 static int promoStyle;
\r
4557 /* Process messages for Promotion dialog box */
\r
4559 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4564 switch (message) {
\r
4566 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4567 /* Center the dialog over the application window */
\r
4568 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4569 Translate(hDlg, DLG_PromotionKing);
\r
4570 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4571 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4572 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4573 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4574 SW_SHOW : SW_HIDE);
\r
4575 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4576 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4577 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4578 PieceToChar(WhiteAngel) != '~') ||
\r
4579 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4580 PieceToChar(BlackAngel) != '~') ) ?
\r
4581 SW_SHOW : SW_HIDE);
\r
4582 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4583 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4584 PieceToChar(WhiteMarshall) != '~') ||
\r
4585 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4586 PieceToChar(BlackMarshall) != '~') ) ?
\r
4587 SW_SHOW : SW_HIDE);
\r
4588 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4589 ShowWindow(GetDlgItem(hDlg, PB_Rook), !promoStyle ? SW_SHOW : SW_HIDE);
\r
4590 ShowWindow(GetDlgItem(hDlg, PB_Bishop), !promoStyle ? SW_SHOW : SW_HIDE);
\r
4592 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4593 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4594 SetWindowText(hDlg, "Promote?");
\r
4596 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4597 gameInfo.variant == VariantSuper ?
\r
4598 SW_SHOW : SW_HIDE);
\r
4601 case WM_COMMAND: /* message: received a command */
\r
4602 switch (LOWORD(wParam)) {
\r
4604 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4605 ClearHighlights();
\r
4606 DrawPosition(FALSE, NULL);
\r
4609 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4612 promoChar = promoStyle ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4615 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4616 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4619 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4620 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4622 case PB_Chancellor:
\r
4623 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4625 case PB_Archbishop:
\r
4626 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4629 promoChar = gameInfo.variant == VariantShogi ? '=' : promoStyle ? NULLCHAR :
\r
4630 ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight));
\r
4635 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4636 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4637 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4638 fromX = fromY = -1;
\r
4639 if (!appData.highlightLastMove) {
\r
4640 ClearHighlights();
\r
4641 DrawPosition(FALSE, NULL);
\r
4648 /* Pop up promotion dialog */
\r
4650 PromotionPopup(HWND hwnd)
\r
4654 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4655 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4656 hwnd, (DLGPROC)lpProc);
\r
4657 FreeProcInstance(lpProc);
\r
4661 PromotionPopUp(char choice)
\r
4663 promoStyle = (choice == '+' || IS_SHOGI(gameInfo.variant));
\r
4664 DrawPosition(TRUE, NULL);
\r
4665 PromotionPopup(hwndMain);
\r
4669 LoadGameDialog(HWND hwnd, char* title)
\r
4673 char fileTitle[MSG_SIZ];
\r
4674 f = OpenFileDialog(hwnd, "rb", "",
\r
4675 appData.oldSaveStyle ? "gam" : "pgn",
\r
4677 title, &number, fileTitle, NULL);
\r
4679 cmailMsgLoaded = FALSE;
\r
4680 if (number == 0) {
\r
4681 int error = GameListBuild(f);
\r
4683 DisplayError(_("Cannot build game list"), error);
\r
4684 } else if (!ListEmpty(&gameList) &&
\r
4685 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4686 GameListPopUp(f, fileTitle);
\r
4689 GameListDestroy();
\r
4692 LoadGame(f, number, fileTitle, FALSE);
\r
4696 int get_term_width()
\r
4701 HFONT hfont, hold_font;
\r
4706 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4710 // get the text metrics
\r
4711 hdc = GetDC(hText);
\r
4712 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4713 if (consoleCF.dwEffects & CFE_BOLD)
\r
4714 lf.lfWeight = FW_BOLD;
\r
4715 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4716 lf.lfItalic = TRUE;
\r
4717 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4718 lf.lfStrikeOut = TRUE;
\r
4719 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4720 lf.lfUnderline = TRUE;
\r
4721 hfont = CreateFontIndirect(&lf);
\r
4722 hold_font = SelectObject(hdc, hfont);
\r
4723 GetTextMetrics(hdc, &tm);
\r
4724 SelectObject(hdc, hold_font);
\r
4725 DeleteObject(hfont);
\r
4726 ReleaseDC(hText, hdc);
\r
4728 // get the rectangle
\r
4729 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4731 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4734 void UpdateICSWidth(HWND hText)
\r
4736 LONG old_width, new_width;
\r
4738 new_width = get_term_width(hText, FALSE);
\r
4739 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4740 if (new_width != old_width)
\r
4742 ics_update_width(new_width);
\r
4743 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4748 ChangedConsoleFont()
\r
4751 CHARRANGE tmpsel, sel;
\r
4752 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4753 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4754 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4757 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4758 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4759 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4760 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4761 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4762 * size. This was undocumented in the version of MSVC++ that I had
\r
4763 * when I wrote the code, but is apparently documented now.
\r
4765 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4766 cfmt.bCharSet = f->lf.lfCharSet;
\r
4767 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4768 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4769 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4770 /* Why are the following seemingly needed too? */
\r
4771 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4772 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4773 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4775 tmpsel.cpMax = -1; /*999999?*/
\r
4776 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4777 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4778 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4779 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4781 paraf.cbSize = sizeof(paraf);
\r
4782 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4783 paraf.dxStartIndent = 0;
\r
4784 paraf.dxOffset = WRAP_INDENT;
\r
4785 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4786 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4787 UpdateICSWidth(hText);
\r
4790 /*---------------------------------------------------------------------------*\
\r
4792 * Window Proc for main window
\r
4794 \*---------------------------------------------------------------------------*/
\r
4796 /* Process messages for main window, etc. */
\r
4798 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4805 char fileTitle[MSG_SIZ];
\r
4806 static SnapData sd;
\r
4807 static int peek=0;
\r
4809 switch (message) {
\r
4811 case WM_PAINT: /* message: repaint portion of window */
\r
4815 case WM_ERASEBKGND:
\r
4816 if (IsIconic(hwnd)) {
\r
4817 /* Cheat; change the message */
\r
4818 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4820 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4824 case WM_LBUTTONDOWN:
\r
4825 case WM_MBUTTONDOWN:
\r
4826 case WM_RBUTTONDOWN:
\r
4827 case WM_LBUTTONUP:
\r
4828 case WM_MBUTTONUP:
\r
4829 case WM_RBUTTONUP:
\r
4830 case WM_MOUSEMOVE:
\r
4831 case WM_MOUSEWHEEL:
\r
4832 MouseEvent(hwnd, message, wParam, lParam);
\r
4836 if((char)wParam == '\b') {
\r
4837 ForwardEvent(); peek = 0;
\r
4840 JAWS_KBUP_NAVIGATION
\r
4845 if((char)wParam == '\b') {
\r
4846 if(!peek) BackwardEvent(), peek = 1;
\r
4849 JAWS_KBDOWN_NAVIGATION
\r
4855 JAWS_ALT_INTERCEPT
\r
4857 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4858 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4859 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4860 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4862 SendMessage(h, message, wParam, lParam);
\r
4863 } else if(lParam != KF_REPEAT) {
\r
4864 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4865 TypeInEvent((char)wParam);
\r
4866 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4867 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4872 case WM_PALETTECHANGED:
\r
4873 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4875 HDC hdc = GetDC(hwndMain);
\r
4876 SelectPalette(hdc, hPal, TRUE);
\r
4877 nnew = RealizePalette(hdc);
\r
4879 paletteChanged = TRUE;
\r
4881 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4883 ReleaseDC(hwnd, hdc);
\r
4887 case WM_QUERYNEWPALETTE:
\r
4888 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4890 HDC hdc = GetDC(hwndMain);
\r
4891 paletteChanged = FALSE;
\r
4892 SelectPalette(hdc, hPal, FALSE);
\r
4893 nnew = RealizePalette(hdc);
\r
4895 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4897 ReleaseDC(hwnd, hdc);
\r
4902 case WM_COMMAND: /* message: command from application menu */
\r
4903 wmId = LOWORD(wParam);
\r
4908 SAY("new game enter a move to play against the computer with white");
\r
4911 case IDM_NewGameFRC:
\r
4912 if( NewGameFRC() == 0 ) {
\r
4917 case IDM_NewVariant:
\r
4918 NewVariantPopup(hwnd);
\r
4921 case IDM_LoadGame:
\r
4922 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4925 case IDM_LoadNextGame:
\r
4929 case IDM_LoadPrevGame:
\r
4933 case IDM_ReloadGame:
\r
4937 case IDM_LoadPosition:
\r
4938 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4939 Reset(FALSE, TRUE);
\r
4942 f = OpenFileDialog(hwnd, "rb", "",
\r
4943 appData.oldSaveStyle ? "pos" : "fen",
\r
4945 _("Load Position from File"), &number, fileTitle, NULL);
\r
4947 LoadPosition(f, number, fileTitle);
\r
4951 case IDM_LoadNextPosition:
\r
4952 ReloadPosition(1);
\r
4955 case IDM_LoadPrevPosition:
\r
4956 ReloadPosition(-1);
\r
4959 case IDM_ReloadPosition:
\r
4960 ReloadPosition(0);
\r
4963 case IDM_SaveGame:
\r
4964 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4965 f = OpenFileDialog(hwnd, "a", defName,
\r
4966 appData.oldSaveStyle ? "gam" : "pgn",
\r
4968 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4970 SaveGame(f, 0, "");
\r
4974 case IDM_SavePosition:
\r
4975 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4976 f = OpenFileDialog(hwnd, "a", defName,
\r
4977 appData.oldSaveStyle ? "pos" : "fen",
\r
4979 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4981 SavePosition(f, 0, "");
\r
4985 case IDM_SaveDiagram:
\r
4986 defName = "diagram";
\r
4987 f = OpenFileDialog(hwnd, "wb", defName,
\r
4990 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
4996 case IDM_SaveSelected:
\r
4997 f = OpenFileDialog(hwnd, "a", "",
\r
5000 _("Save Game to File"), NULL, fileTitle, NULL);
\r
5002 SaveSelected(f, 0, "");
\r
5006 case IDM_CreateBook:
\r
5007 CreateBookEvent();
\r
5010 case IDM_CopyGame:
\r
5011 CopyGameToClipboard();
\r
5014 case IDM_PasteGame:
\r
5015 PasteGameFromClipboard();
\r
5018 case IDM_CopyGameListToClipboard:
\r
5019 CopyGameListToClipboard();
\r
5022 /* [AS] Autodetect FEN or PGN data */
\r
5023 case IDM_PasteAny:
\r
5024 PasteGameOrFENFromClipboard();
\r
5027 /* [AS] Move history */
\r
5028 case IDM_ShowMoveHistory:
\r
5029 if( MoveHistoryIsUp() ) {
\r
5030 MoveHistoryPopDown();
\r
5033 MoveHistoryPopUp();
\r
5037 /* [AS] Eval graph */
\r
5038 case IDM_ShowEvalGraph:
\r
5039 if( EvalGraphIsUp() ) {
\r
5040 EvalGraphPopDown();
\r
5044 SetFocus(hwndMain);
\r
5048 /* [AS] Engine output */
\r
5049 case IDM_ShowEngineOutput:
\r
5050 if( EngineOutputIsUp() ) {
\r
5051 EngineOutputPopDown();
\r
5054 EngineOutputPopUp();
\r
5058 /* [AS] User adjudication */
\r
5059 case IDM_UserAdjudication_White:
\r
5060 UserAdjudicationEvent( +1 );
\r
5063 case IDM_UserAdjudication_Black:
\r
5064 UserAdjudicationEvent( -1 );
\r
5067 case IDM_UserAdjudication_Draw:
\r
5068 UserAdjudicationEvent( 0 );
\r
5071 /* [AS] Game list options dialog */
\r
5072 case IDM_GameListOptions:
\r
5073 GameListOptions();
\r
5080 case IDM_CopyPosition:
\r
5081 CopyFENToClipboard();
\r
5084 case IDM_PastePosition:
\r
5085 PasteFENFromClipboard();
\r
5088 case IDM_MailMove:
\r
5092 case IDM_ReloadCMailMsg:
\r
5093 Reset(TRUE, TRUE);
\r
5094 ReloadCmailMsgEvent(FALSE);
\r
5097 case IDM_Minimize:
\r
5098 ShowWindow(hwnd, SW_MINIMIZE);
\r
5105 case IDM_MachineWhite:
\r
5106 MachineWhiteEvent();
\r
5108 * refresh the tags dialog only if it's visible
\r
5110 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
5112 tags = PGNTags(&gameInfo);
\r
5113 TagsPopUp(tags, CmailMsg());
\r
5116 SAY("computer starts playing white");
\r
5119 case IDM_MachineBlack:
\r
5120 MachineBlackEvent();
\r
5122 * refresh the tags dialog only if it's visible
\r
5124 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
5126 tags = PGNTags(&gameInfo);
\r
5127 TagsPopUp(tags, CmailMsg());
\r
5130 SAY("computer starts playing black");
\r
5133 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
5134 if(matchMode) EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_GRAYED);
\r
5135 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
5138 case IDM_TwoMachines:
\r
5139 TwoMachinesEvent();
\r
5142 * refresh the tags dialog only if it's visible
\r
5144 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
5146 tags = PGNTags(&gameInfo);
\r
5147 TagsPopUp(tags, CmailMsg());
\r
5150 SAY("computer starts playing both sides");
\r
5153 case IDM_AnalysisMode:
\r
5154 if(AnalyzeModeEvent()) {
\r
5155 SAY("analyzing current position");
\r
5159 case IDM_AnalyzeFile:
\r
5160 AnalyzeFileEvent();
\r
5163 case IDM_IcsClient:
\r
5167 case IDM_EditGame:
\r
5168 case IDM_EditGame2:
\r
5173 case IDM_EditPosition:
\r
5174 case IDM_EditPosition2:
\r
5175 EditPositionEvent();
\r
5176 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
5179 case IDM_Training:
\r
5183 case IDM_ShowGameList:
\r
5184 ShowGameListProc();
\r
5187 case IDM_EditProgs1:
\r
5188 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
5191 case IDM_LoadProg1:
\r
5192 LoadEnginePopUp(hwndMain, 0);
\r
5195 case IDM_LoadProg2:
\r
5196 LoadEnginePopUp(hwndMain, 1);
\r
5199 case IDM_EditServers:
\r
5200 EditTagsPopUp(icsNames, &icsNames);
\r
5203 case IDM_EditTags:
\r
5208 case IDM_EditBook:
\r
5212 case IDM_EditComment:
\r
5214 if (commentUp && editComment) {
\r
5217 EditCommentEvent();
\r
5238 case IDM_CallFlag:
\r
5258 case IDM_StopObserving:
\r
5259 StopObservingEvent();
\r
5262 case IDM_StopExamining:
\r
5263 StopExaminingEvent();
\r
5267 UploadGameEvent();
\r
5270 case IDM_TypeInMove:
\r
5271 TypeInEvent('\000');
\r
5274 case IDM_TypeInName:
\r
5275 PopUpNameDialog('\000');
\r
5278 case IDM_Backward:
\r
5280 SetFocus(hwndMain);
\r
5287 SetFocus(hwndMain);
\r
5292 SetFocus(hwndMain);
\r
5297 SetFocus(hwndMain);
\r
5300 case OPT_GameListNext: // [HGM] forward these two accelerators to Game List
\r
5301 case OPT_GameListPrev:
\r
5302 if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);
\r
5306 RevertEvent(FALSE);
\r
5309 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5310 RevertEvent(TRUE);
\r
5313 case IDM_TruncateGame:
\r
5314 TruncateGameEvent();
\r
5321 case IDM_RetractMove:
\r
5322 RetractMoveEvent();
\r
5325 case IDM_FlipView:
\r
5326 flipView = !flipView;
\r
5327 DrawPosition(FALSE, NULL);
\r
5330 case IDM_FlipClock:
\r
5331 flipClock = !flipClock;
\r
5332 DisplayBothClocks();
\r
5336 case IDM_MuteSounds:
\r
5337 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5338 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5339 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5342 case IDM_GeneralOptions:
\r
5343 GeneralOptionsPopup(hwnd);
\r
5344 DrawPosition(TRUE, NULL);
\r
5347 case IDM_BoardOptions:
\r
5348 BoardOptionsPopup(hwnd);
\r
5351 case IDM_ThemeOptions:
\r
5352 ThemeOptionsPopup(hwnd);
\r
5355 case IDM_EnginePlayOptions:
\r
5356 EnginePlayOptionsPopup(hwnd);
\r
5359 case IDM_Engine1Options:
\r
5360 EngineOptionsPopup(hwnd, &first);
\r
5363 case IDM_Engine2Options:
\r
5365 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5366 EngineOptionsPopup(hwnd, &second);
\r
5369 case IDM_OptionsUCI:
\r
5370 UciOptionsPopup(hwnd);
\r
5374 TourneyPopup(hwnd);
\r
5377 case IDM_IcsOptions:
\r
5378 IcsOptionsPopup(hwnd);
\r
5382 FontsOptionsPopup(hwnd);
\r
5386 SoundOptionsPopup(hwnd);
\r
5389 case IDM_CommPort:
\r
5390 CommPortOptionsPopup(hwnd);
\r
5393 case IDM_LoadOptions:
\r
5394 LoadOptionsPopup(hwnd);
\r
5397 case IDM_SaveOptions:
\r
5398 SaveOptionsPopup(hwnd);
\r
5401 case IDM_TimeControl:
\r
5402 TimeControlOptionsPopup(hwnd);
\r
5405 case IDM_SaveSettings:
\r
5406 SaveSettings(settingsFileName);
\r
5409 case IDM_SaveSettingsOnExit:
\r
5410 saveSettingsOnExit = !saveSettingsOnExit;
\r
5411 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5412 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5413 MF_CHECKED : MF_UNCHECKED));
\r
5424 case IDM_AboutGame:
\r
5429 appData.debugMode = !appData.debugMode;
\r
5430 if (appData.debugMode) {
\r
5431 char dir[MSG_SIZ];
\r
5432 GetCurrentDirectory(MSG_SIZ, dir);
\r
5433 SetCurrentDirectory(installDir);
\r
5434 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5435 SetCurrentDirectory(dir);
\r
5436 setbuf(debugFP, NULL);
\r
5443 case IDM_HELPCONTENTS:
\r
5444 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5445 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5446 MessageBox (GetFocus(),
\r
5447 _("Unable to activate help"),
\r
5448 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5452 case IDM_HELPSEARCH:
\r
5453 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5454 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5455 MessageBox (GetFocus(),
\r
5456 _("Unable to activate help"),
\r
5457 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5461 case IDM_HELPHELP:
\r
5462 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5463 MessageBox (GetFocus(),
\r
5464 _("Unable to activate help"),
\r
5465 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5470 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5472 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5473 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5474 FreeProcInstance(lpProc);
\r
5477 case IDM_DirectCommand1:
\r
5478 AskQuestionEvent(_("Direct Command"),
\r
5479 _("Send to chess program:"), "", "1");
\r
5481 case IDM_DirectCommand2:
\r
5482 AskQuestionEvent(_("Direct Command"),
\r
5483 _("Send to second chess program:"), "", "2");
\r
5486 case EP_WhitePawn:
\r
5487 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5488 fromX = fromY = -1;
\r
5491 case EP_WhiteKnight:
\r
5492 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5493 fromX = fromY = -1;
\r
5496 case EP_WhiteBishop:
\r
5497 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5498 fromX = fromY = -1;
\r
5501 case EP_WhiteRook:
\r
5502 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5503 fromX = fromY = -1;
\r
5506 case EP_WhiteQueen:
\r
5507 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5508 fromX = fromY = -1;
\r
5511 case EP_WhiteFerz:
\r
5512 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5513 fromX = fromY = -1;
\r
5516 case EP_WhiteWazir:
\r
5517 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5518 fromX = fromY = -1;
\r
5521 case EP_WhiteAlfil:
\r
5522 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5523 fromX = fromY = -1;
\r
5526 case EP_WhiteCannon:
\r
5527 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5528 fromX = fromY = -1;
\r
5531 case EP_WhiteCardinal:
\r
5532 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5533 fromX = fromY = -1;
\r
5536 case EP_WhiteMarshall:
\r
5537 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5538 fromX = fromY = -1;
\r
5541 case EP_WhiteKing:
\r
5542 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5543 fromX = fromY = -1;
\r
5546 case EP_BlackPawn:
\r
5547 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5548 fromX = fromY = -1;
\r
5551 case EP_BlackKnight:
\r
5552 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5553 fromX = fromY = -1;
\r
5556 case EP_BlackBishop:
\r
5557 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5558 fromX = fromY = -1;
\r
5561 case EP_BlackRook:
\r
5562 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5563 fromX = fromY = -1;
\r
5566 case EP_BlackQueen:
\r
5567 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5568 fromX = fromY = -1;
\r
5571 case EP_BlackFerz:
\r
5572 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5573 fromX = fromY = -1;
\r
5576 case EP_BlackWazir:
\r
5577 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5578 fromX = fromY = -1;
\r
5581 case EP_BlackAlfil:
\r
5582 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5583 fromX = fromY = -1;
\r
5586 case EP_BlackCannon:
\r
5587 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5588 fromX = fromY = -1;
\r
5591 case EP_BlackCardinal:
\r
5592 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5593 fromX = fromY = -1;
\r
5596 case EP_BlackMarshall:
\r
5597 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5598 fromX = fromY = -1;
\r
5601 case EP_BlackKing:
\r
5602 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5603 fromX = fromY = -1;
\r
5606 case EP_EmptySquare:
\r
5607 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5608 fromX = fromY = -1;
\r
5611 case EP_ClearBoard:
\r
5612 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5613 fromX = fromY = -1;
\r
5617 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5618 fromX = fromY = -1;
\r
5622 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5623 fromX = fromY = -1;
\r
5627 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5628 fromX = fromY = -1;
\r
5632 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5633 fromX = fromY = -1;
\r
5637 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5638 fromX = fromY = -1;
\r
5642 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5643 fromX = fromY = -1;
\r
5647 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5648 fromX = fromY = -1;
\r
5652 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5653 fromX = fromY = -1;
\r
5657 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5658 fromX = fromY = -1;
\r
5662 barbaric = 0; appData.language = "";
\r
5663 TranslateMenus(0);
\r
5664 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5665 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5666 lastChecked = wmId;
\r
5670 if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)
\r
5671 RecentEngineEvent(wmId - IDM_RecentEngines);
\r
5673 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5674 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5675 TranslateMenus(0);
\r
5676 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5677 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5678 lastChecked = wmId;
\r
5681 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5687 case CLOCK_TIMER_ID:
\r
5688 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5689 clockTimerEvent = 0;
\r
5690 DecrementClocks(); /* call into back end */
\r
5692 case LOAD_GAME_TIMER_ID:
\r
5693 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5694 loadGameTimerEvent = 0;
\r
5695 AutoPlayGameLoop(); /* call into back end */
\r
5697 case ANALYSIS_TIMER_ID:
\r
5698 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5699 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5700 AnalysisPeriodicEvent(0);
\r
5702 KillTimer(hwnd, analysisTimerEvent);
\r
5703 analysisTimerEvent = 0;
\r
5706 case DELAYED_TIMER_ID:
\r
5707 KillTimer(hwnd, delayedTimerEvent);
\r
5708 delayedTimerEvent = 0;
\r
5709 delayedTimerCallback();
\r
5714 case WM_USER_Input:
\r
5715 InputEvent(hwnd, message, wParam, lParam);
\r
5718 /* [AS] Also move "attached" child windows */
\r
5719 case WM_WINDOWPOSCHANGING:
\r
5721 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5722 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5724 if( ((lpwp->flags & SWP_NOMOVE) == 0) /*&& ((lpwp->flags & SWP_NOSIZE) != 0)*/ ) { // [HGM] in Win8 size always accompanies move?
\r
5725 /* Window is moving */
\r
5728 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5729 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5730 rcMain.right = wpMain.x + wpMain.width;
\r
5731 rcMain.top = wpMain.y;
\r
5732 rcMain.bottom = wpMain.y + wpMain.height;
\r
5734 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5735 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5736 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5737 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5738 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5739 wpMain.x = lpwp->x;
\r
5740 wpMain.y = lpwp->y;
\r
5746 /* [AS] Snapping */
\r
5747 case WM_ENTERSIZEMOVE:
\r
5748 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5749 if (hwnd == hwndMain) {
\r
5750 doingSizing = TRUE;
\r
5753 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5757 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5758 if (hwnd == hwndMain) {
\r
5759 lastSizing = wParam;
\r
5764 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5765 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5767 case WM_EXITSIZEMOVE:
\r
5768 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5769 if (hwnd == hwndMain) {
\r
5771 doingSizing = FALSE;
\r
5772 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5773 GetClientRect(hwnd, &client);
\r
5774 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5776 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5778 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5781 case WM_DESTROY: /* message: window being destroyed */
\r
5782 PostQuitMessage(0);
\r
5786 if (hwnd == hwndMain) {
\r
5791 default: /* Passes it on if unprocessed */
\r
5792 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5799 /*---------------------------------------------------------------------------*\
\r
5801 * Misc utility routines
\r
5803 \*---------------------------------------------------------------------------*/
\r
5806 * Decent random number generator, at least not as bad as Windows
\r
5807 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5809 unsigned int randstate;
\r
5814 randstate = randstate * 1664525 + 1013904223;
\r
5815 return (int) randstate & 0x7fffffff;
\r
5819 mysrandom(unsigned int seed)
\r
5826 * returns TRUE if user selects a different color, FALSE otherwise
\r
5830 ChangeColor(HWND hwnd, COLORREF *which)
\r
5832 static BOOL firstTime = TRUE;
\r
5833 static DWORD customColors[16];
\r
5835 COLORREF newcolor;
\r
5840 /* Make initial colors in use available as custom colors */
\r
5841 /* Should we put the compiled-in defaults here instead? */
\r
5843 customColors[i++] = lightSquareColor & 0xffffff;
\r
5844 customColors[i++] = darkSquareColor & 0xffffff;
\r
5845 customColors[i++] = whitePieceColor & 0xffffff;
\r
5846 customColors[i++] = blackPieceColor & 0xffffff;
\r
5847 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5848 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5850 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5851 customColors[i++] = textAttribs[ccl].color;
\r
5853 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5854 firstTime = FALSE;
\r
5857 cc.lStructSize = sizeof(cc);
\r
5858 cc.hwndOwner = hwnd;
\r
5859 cc.hInstance = NULL;
\r
5860 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5861 cc.lpCustColors = (LPDWORD) customColors;
\r
5862 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5864 if (!ChooseColor(&cc)) return FALSE;
\r
5866 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5867 if (newcolor == *which) return FALSE;
\r
5868 *which = newcolor;
\r
5872 InitDrawingColors();
\r
5873 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5878 MyLoadSound(MySound *ms)
\r
5884 if (ms->data && ms->flag) free(ms->data);
\r
5887 switch (ms->name[0]) {
\r
5893 /* System sound from Control Panel. Don't preload here. */
\r
5897 if (ms->name[1] == NULLCHAR) {
\r
5898 /* "!" alone = silence */
\r
5901 /* Builtin wave resource. Error if not found. */
\r
5902 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5903 if (h == NULL) break;
\r
5904 ms->data = (void *)LoadResource(hInst, h);
\r
5905 ms->flag = 0; // not maloced, so cannot be freed!
\r
5906 if (h == NULL) break;
\r
5911 /* .wav file. Error if not found. */
\r
5912 f = fopen(ms->name, "rb");
\r
5913 if (f == NULL) break;
\r
5914 if (fstat(fileno(f), &st) < 0) break;
\r
5915 ms->data = malloc(st.st_size);
\r
5917 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5923 char buf[MSG_SIZ];
\r
5924 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5925 DisplayError(buf, GetLastError());
\r
5931 MyPlaySound(MySound *ms)
\r
5933 BOOLEAN ok = FALSE;
\r
5935 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5936 switch (ms->name[0]) {
\r
5938 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5943 /* System sound from Control Panel (deprecated feature).
\r
5944 "$" alone or an unset sound name gets default beep (still in use). */
\r
5945 if (ms->name[1]) {
\r
5946 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5948 if (!ok) ok = MessageBeep(MB_OK);
\r
5951 /* Builtin wave resource, or "!" alone for silence */
\r
5952 if (ms->name[1]) {
\r
5953 if (ms->data == NULL) return FALSE;
\r
5954 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5960 /* .wav file. Error if not found. */
\r
5961 if (ms->data == NULL) return FALSE;
\r
5962 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5965 /* Don't print an error: this can happen innocently if the sound driver
\r
5966 is busy; for instance, if another instance of WinBoard is playing
\r
5967 a sound at about the same time. */
\r
5973 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5976 OPENFILENAME *ofn;
\r
5977 static UINT *number; /* gross that this is static */
\r
5979 switch (message) {
\r
5980 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5981 /* Center the dialog over the application window */
\r
5982 ofn = (OPENFILENAME *) lParam;
\r
5983 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5984 number = (UINT *) ofn->lCustData;
\r
5985 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5989 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5990 Translate(hDlg, 1536);
\r
5991 return FALSE; /* Allow for further processing */
\r
5994 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5995 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5997 return FALSE; /* Allow for further processing */
\r
6003 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
6005 static UINT *number;
\r
6006 OPENFILENAME *ofname;
\r
6009 case WM_INITDIALOG:
\r
6010 Translate(hdlg, DLG_IndexNumber);
\r
6011 ofname = (OPENFILENAME *)lParam;
\r
6012 number = (UINT *)(ofname->lCustData);
\r
6015 ofnot = (OFNOTIFY *)lParam;
\r
6016 if (ofnot->hdr.code == CDN_FILEOK) {
\r
6017 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
6026 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
6027 char *nameFilt, char *dlgTitle, UINT *number,
\r
6028 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
6030 OPENFILENAME openFileName;
\r
6031 char buf1[MSG_SIZ];
\r
6034 if (fileName == NULL) fileName = buf1;
\r
6035 if (defName == NULL) {
\r
6036 safeStrCpy(fileName, "*.", 3 );
\r
6037 strcat(fileName, defExt);
\r
6039 safeStrCpy(fileName, defName, MSG_SIZ );
\r
6041 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
6042 if (number) *number = 0;
\r
6044 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
6045 openFileName.hwndOwner = hwnd;
\r
6046 openFileName.hInstance = (HANDLE) hInst;
\r
6047 openFileName.lpstrFilter = nameFilt;
\r
6048 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
6049 openFileName.nMaxCustFilter = 0L;
\r
6050 openFileName.nFilterIndex = 1L;
\r
6051 openFileName.lpstrFile = fileName;
\r
6052 openFileName.nMaxFile = MSG_SIZ;
\r
6053 openFileName.lpstrFileTitle = fileTitle;
\r
6054 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
6055 openFileName.lpstrInitialDir = NULL;
\r
6056 openFileName.lpstrTitle = dlgTitle;
\r
6057 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
6058 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
6059 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
6060 | (oldDialog ? 0 : OFN_EXPLORER);
\r
6061 openFileName.nFileOffset = 0;
\r
6062 openFileName.nFileExtension = 0;
\r
6063 openFileName.lpstrDefExt = defExt;
\r
6064 openFileName.lCustData = (LONG) number;
\r
6065 openFileName.lpfnHook = oldDialog ?
\r
6066 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
6067 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
6069 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
6070 GetOpenFileName(&openFileName)) {
\r
6071 /* open the file */
\r
6072 f = fopen(openFileName.lpstrFile, write);
\r
6074 MessageBox(hwnd, _("File open failed"), NULL,
\r
6075 MB_OK|MB_ICONEXCLAMATION);
\r
6079 int err = CommDlgExtendedError();
\r
6080 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
6089 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
6091 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
6094 * Get the first pop-up menu in the menu template. This is the
\r
6095 * menu that TrackPopupMenu displays.
\r
6097 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
6098 TranslateOneMenu(10, hmenuTrackPopup);
\r
6100 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
6103 * TrackPopup uses screen coordinates, so convert the
\r
6104 * coordinates of the mouse click to screen coordinates.
\r
6106 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
6108 /* Draw and track the floating pop-up menu. */
\r
6109 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
6110 pt.x, pt.y, 0, hwnd, NULL);
\r
6112 /* Destroy the menu.*/
\r
6113 DestroyMenu(hmenu);
\r
6118 int sizeX, sizeY, newSizeX, newSizeY;
\r
6120 } ResizeEditPlusButtonsClosure;
\r
6123 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
6125 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
6129 if (hChild == cl->hText) return TRUE;
\r
6130 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
6131 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
6132 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
6133 ScreenToClient(cl->hDlg, &pt);
\r
6134 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
6135 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
6139 /* Resize a dialog that has a (rich) edit field filling most of
\r
6140 the top, with a row of buttons below */
\r
6142 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
6145 int newTextHeight, newTextWidth;
\r
6146 ResizeEditPlusButtonsClosure cl;
\r
6148 /*if (IsIconic(hDlg)) return;*/
\r
6149 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
6151 cl.hdwp = BeginDeferWindowPos(8);
\r
6153 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
6154 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6155 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6156 if (newTextHeight < 0) {
\r
6157 newSizeY += -newTextHeight;
\r
6158 newTextHeight = 0;
\r
6160 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
6161 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6167 cl.newSizeX = newSizeX;
\r
6168 cl.newSizeY = newSizeY;
\r
6169 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
6171 EndDeferWindowPos(cl.hdwp);
\r
6174 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
6176 RECT rChild, rParent;
\r
6177 int wChild, hChild, wParent, hParent;
\r
6178 int wScreen, hScreen, xNew, yNew;
\r
6181 /* Get the Height and Width of the child window */
\r
6182 GetWindowRect (hwndChild, &rChild);
\r
6183 wChild = rChild.right - rChild.left;
\r
6184 hChild = rChild.bottom - rChild.top;
\r
6186 /* Get the Height and Width of the parent window */
\r
6187 GetWindowRect (hwndParent, &rParent);
\r
6188 wParent = rParent.right - rParent.left;
\r
6189 hParent = rParent.bottom - rParent.top;
\r
6191 /* Get the display limits */
\r
6192 hdc = GetDC (hwndChild);
\r
6193 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
6194 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
6195 ReleaseDC(hwndChild, hdc);
\r
6197 /* Calculate new X position, then adjust for screen */
\r
6198 xNew = rParent.left + ((wParent - wChild) /2);
\r
6201 } else if ((xNew+wChild) > wScreen) {
\r
6202 xNew = wScreen - wChild;
\r
6205 /* Calculate new Y position, then adjust for screen */
\r
6207 yNew = rParent.top + ((hParent - hChild) /2);
\r
6210 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
6215 } else if ((yNew+hChild) > hScreen) {
\r
6216 yNew = hScreen - hChild;
\r
6219 /* Set it, and return */
\r
6220 return SetWindowPos (hwndChild, NULL,
\r
6221 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
6224 /* Center one window over another */
\r
6225 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
6227 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
6230 /*---------------------------------------------------------------------------*\
\r
6232 * Startup Dialog functions
\r
6234 \*---------------------------------------------------------------------------*/
\r
6236 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
6238 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6240 while (*cd != NULL) {
\r
6241 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
6247 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
6249 char buf1[MAX_ARG_LEN];
\r
6252 if (str[0] == '@') {
\r
6253 FILE* f = fopen(str + 1, "r");
\r
6255 DisplayFatalError(str + 1, errno, 2);
\r
6258 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
6260 buf1[len] = NULLCHAR;
\r
6264 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6267 char buf[MSG_SIZ];
\r
6268 char *end = strchr(str, '\n');
\r
6269 if (end == NULL) return;
\r
6270 memcpy(buf, str, end - str);
\r
6271 buf[end - str] = NULLCHAR;
\r
6272 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6278 SetStartupDialogEnables(HWND hDlg)
\r
6280 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6281 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6282 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6283 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6284 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6285 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6286 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6287 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6288 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6289 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6290 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6291 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6292 IsDlgButtonChecked(hDlg, OPT_View));
\r
6296 QuoteForFilename(char *filename)
\r
6298 int dquote, space;
\r
6299 dquote = strchr(filename, '"') != NULL;
\r
6300 space = strchr(filename, ' ') != NULL;
\r
6301 if (dquote || space) {
\r
6313 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6315 char buf[MSG_SIZ];
\r
6318 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6319 q = QuoteForFilename(nthcp);
\r
6320 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6321 if (*nthdir != NULLCHAR) {
\r
6322 q = QuoteForFilename(nthdir);
\r
6323 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6325 if (*nthcp == NULLCHAR) {
\r
6326 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6327 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6328 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6329 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6334 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6336 char buf[MSG_SIZ];
\r
6340 switch (message) {
\r
6341 case WM_INITDIALOG:
\r
6342 /* Center the dialog */
\r
6343 CenterWindow (hDlg, GetDesktopWindow());
\r
6344 Translate(hDlg, DLG_Startup);
\r
6345 /* Initialize the dialog items */
\r
6346 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6347 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6348 firstChessProgramNames);
\r
6349 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6350 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6351 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6352 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6353 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6354 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6355 if (*appData.icsHelper != NULLCHAR) {
\r
6356 char *q = QuoteForFilename(appData.icsHelper);
\r
6357 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6359 if (*appData.icsHost == NULLCHAR) {
\r
6360 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6361 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6362 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6363 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6364 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6367 if (appData.icsActive) {
\r
6368 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6370 else if (appData.noChessProgram) {
\r
6371 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6374 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6377 SetStartupDialogEnables(hDlg);
\r
6381 switch (LOWORD(wParam)) {
\r
6383 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6384 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6385 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6387 comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox
\r
6388 ParseArgs(StringGet, &p);
\r
6389 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6390 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6392 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6393 ParseArgs(StringGet, &p);
\r
6394 SwapEngines(singleList); // ... and then make it 'second'
\r
6396 appData.noChessProgram = FALSE;
\r
6397 appData.icsActive = FALSE;
\r
6398 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6399 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6400 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6402 ParseArgs(StringGet, &p);
\r
6403 if (appData.zippyPlay) {
\r
6404 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6405 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6407 ParseArgs(StringGet, &p);
\r
6409 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6410 appData.noChessProgram = TRUE;
\r
6411 appData.icsActive = FALSE;
\r
6413 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6414 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6417 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6418 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6420 ParseArgs(StringGet, &p);
\r
6422 EndDialog(hDlg, TRUE);
\r
6429 case IDM_HELPCONTENTS:
\r
6430 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6431 MessageBox (GetFocus(),
\r
6432 _("Unable to activate help"),
\r
6433 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6438 SetStartupDialogEnables(hDlg);
\r
6446 /*---------------------------------------------------------------------------*\
\r
6448 * About box dialog functions
\r
6450 \*---------------------------------------------------------------------------*/
\r
6452 /* Process messages for "About" dialog box */
\r
6454 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6456 switch (message) {
\r
6457 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6458 /* Center the dialog over the application window */
\r
6459 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6460 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6461 Translate(hDlg, ABOUTBOX);
\r
6465 case WM_COMMAND: /* message: received a command */
\r
6466 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6467 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6468 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6476 /*---------------------------------------------------------------------------*\
\r
6478 * Comment Dialog functions
\r
6480 \*---------------------------------------------------------------------------*/
\r
6483 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6485 static HANDLE hwndText = NULL;
\r
6486 int len, newSizeX, newSizeY;
\r
6487 static int sizeX, sizeY;
\r
6492 switch (message) {
\r
6493 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6494 /* Initialize the dialog items */
\r
6495 Translate(hDlg, DLG_EditComment);
\r
6496 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6497 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6498 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6499 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6500 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6501 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6502 SetWindowText(hDlg, commentTitle);
\r
6503 if (editComment) {
\r
6504 SetFocus(hwndText);
\r
6506 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6508 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6509 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6510 MAKELPARAM(FALSE, 0));
\r
6511 /* Size and position the dialog */
\r
6512 if (!commentDialog) {
\r
6513 commentDialog = hDlg;
\r
6514 GetClientRect(hDlg, &rect);
\r
6515 sizeX = rect.right;
\r
6516 sizeY = rect.bottom;
\r
6517 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6518 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6519 WINDOWPLACEMENT wp;
\r
6520 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6521 wp.length = sizeof(WINDOWPLACEMENT);
\r
6523 wp.showCmd = SW_SHOW;
\r
6524 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6525 wp.rcNormalPosition.left = wpComment.x;
\r
6526 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6527 wp.rcNormalPosition.top = wpComment.y;
\r
6528 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6529 SetWindowPlacement(hDlg, &wp);
\r
6531 GetClientRect(hDlg, &rect);
\r
6532 newSizeX = rect.right;
\r
6533 newSizeY = rect.bottom;
\r
6534 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6535 newSizeX, newSizeY);
\r
6540 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6543 case WM_COMMAND: /* message: received a command */
\r
6544 switch (LOWORD(wParam)) {
\r
6546 if (editComment) {
\r
6548 /* Read changed options from the dialog box */
\r
6549 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6550 len = GetWindowTextLength(hwndText);
\r
6551 str = (char *) malloc(len + 1);
\r
6552 GetWindowText(hwndText, str, len + 1);
\r
6561 ReplaceComment(commentIndex, str);
\r
6568 case OPT_CancelComment:
\r
6572 case OPT_ClearComment:
\r
6573 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6576 case OPT_EditComment:
\r
6577 EditCommentEvent();
\r
6585 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6586 if( wParam == OPT_CommentText ) {
\r
6587 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6589 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6590 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6594 pt.x = LOWORD( lpMF->lParam );
\r
6595 pt.y = HIWORD( lpMF->lParam );
\r
6597 if(lpMF->msg == WM_CHAR) {
\r
6599 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6600 index = sel.cpMin;
\r
6602 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6604 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6605 len = GetWindowTextLength(hwndText);
\r
6606 str = (char *) malloc(len + 1);
\r
6607 GetWindowText(hwndText, str, len + 1);
\r
6608 ReplaceComment(commentIndex, str);
\r
6609 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6610 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6613 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6614 lpMF->msg = WM_USER;
\r
6622 newSizeX = LOWORD(lParam);
\r
6623 newSizeY = HIWORD(lParam);
\r
6624 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6629 case WM_GETMINMAXINFO:
\r
6630 /* Prevent resizing window too small */
\r
6631 mmi = (MINMAXINFO *) lParam;
\r
6632 mmi->ptMinTrackSize.x = 100;
\r
6633 mmi->ptMinTrackSize.y = 100;
\r
6640 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6645 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6647 if (str == NULL) str = "";
\r
6648 p = (char *) malloc(2 * strlen(str) + 2);
\r
6651 if (*str == '\n') *q++ = '\r';
\r
6655 if (commentText != NULL) free(commentText);
\r
6657 commentIndex = index;
\r
6658 commentTitle = title;
\r
6660 editComment = edit;
\r
6662 if (commentDialog) {
\r
6663 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6664 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6666 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6667 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6668 hwndMain, (DLGPROC)lpProc);
\r
6669 FreeProcInstance(lpProc);
\r
6675 /*---------------------------------------------------------------------------*\
\r
6677 * Type-in move dialog functions
\r
6679 \*---------------------------------------------------------------------------*/
\r
6682 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6684 char move[MSG_SIZ];
\r
6687 switch (message) {
\r
6688 case WM_INITDIALOG:
\r
6689 move[0] = (char) lParam;
\r
6690 move[1] = NULLCHAR;
\r
6691 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6692 Translate(hDlg, DLG_TypeInMove);
\r
6693 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6694 SetWindowText(hInput, move);
\r
6696 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6700 switch (LOWORD(wParam)) {
\r
6703 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6704 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6705 TypeInDoneEvent(move);
\r
6706 EndDialog(hDlg, TRUE);
\r
6709 EndDialog(hDlg, FALSE);
\r
6720 PopUpMoveDialog(char firstchar)
\r
6724 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6725 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6726 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6727 FreeProcInstance(lpProc);
\r
6730 /*---------------------------------------------------------------------------*\
\r
6732 * Type-in name dialog functions
\r
6734 \*---------------------------------------------------------------------------*/
\r
6737 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6739 char move[MSG_SIZ];
\r
6742 switch (message) {
\r
6743 case WM_INITDIALOG:
\r
6744 move[0] = (char) lParam;
\r
6745 move[1] = NULLCHAR;
\r
6746 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6747 Translate(hDlg, DLG_TypeInName);
\r
6748 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6749 SetWindowText(hInput, move);
\r
6751 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6755 switch (LOWORD(wParam)) {
\r
6757 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6758 appData.userName = strdup(move);
\r
6759 SetUserLogo(); DisplayLogos();
\r
6761 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6762 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6763 DisplayTitle(move);
\r
6767 EndDialog(hDlg, TRUE);
\r
6770 EndDialog(hDlg, FALSE);
\r
6781 PopUpNameDialog(char firstchar)
\r
6785 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6786 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6787 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6788 FreeProcInstance(lpProc);
\r
6791 /*---------------------------------------------------------------------------*\
\r
6795 \*---------------------------------------------------------------------------*/
\r
6797 /* Nonmodal error box */
\r
6798 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6799 WPARAM wParam, LPARAM lParam);
\r
6802 ErrorPopUp(char *title, char *content)
\r
6806 BOOLEAN modal = hwndMain == NULL;
\r
6824 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6825 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6828 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6830 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6831 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6832 hwndMain, (DLGPROC)lpProc);
\r
6833 FreeProcInstance(lpProc);
\r
6840 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6841 if (errorDialog == NULL) return;
\r
6842 DestroyWindow(errorDialog);
\r
6843 errorDialog = NULL;
\r
6844 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6848 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6852 switch (message) {
\r
6853 case WM_INITDIALOG:
\r
6854 GetWindowRect(hDlg, &rChild);
\r
6857 SetWindowPos(hDlg, NULL, rChild.left,
\r
6858 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6859 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6863 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6864 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6865 and it doesn't work when you resize the dialog.
\r
6866 For now, just give it a default position.
\r
6868 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6869 Translate(hDlg, DLG_Error);
\r
6871 errorDialog = hDlg;
\r
6872 SetWindowText(hDlg, errorTitle);
\r
6873 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6877 switch (LOWORD(wParam)) {
\r
6880 if (errorDialog == hDlg) errorDialog = NULL;
\r
6881 DestroyWindow(hDlg);
\r
6893 HWND gothicDialog = NULL;
\r
6896 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6899 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6901 switch (message) {
\r
6902 case WM_INITDIALOG:
\r
6903 GetWindowRect(hDlg, &rChild);
\r
6905 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6909 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6910 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6911 and it doesn't work when you resize the dialog.
\r
6912 For now, just give it a default position.
\r
6914 gothicDialog = hDlg;
\r
6915 SetWindowText(hDlg, errorTitle);
\r
6916 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6920 switch (LOWORD(wParam)) {
\r
6923 if (errorDialog == hDlg) errorDialog = NULL;
\r
6924 DestroyWindow(hDlg);
\r
6936 GothicPopUp(char *title, VariantClass variant)
\r
6939 static char *lastTitle;
\r
6941 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6942 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6944 if(lastTitle != title && gothicDialog != NULL) {
\r
6945 DestroyWindow(gothicDialog);
\r
6946 gothicDialog = NULL;
\r
6948 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6949 title = lastTitle;
\r
6950 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6951 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6952 hwndMain, (DLGPROC)lpProc);
\r
6953 FreeProcInstance(lpProc);
\r
6958 /*---------------------------------------------------------------------------*\
\r
6960 * Ics Interaction console functions
\r
6962 \*---------------------------------------------------------------------------*/
\r
6964 #define HISTORY_SIZE 64
\r
6965 static char *history[HISTORY_SIZE];
\r
6966 int histIn = 0, histP = 0;
\r
6970 SaveInHistory(char *cmd)
\r
6972 if (history[histIn] != NULL) {
\r
6973 free(history[histIn]);
\r
6974 history[histIn] = NULL;
\r
6976 if (*cmd == NULLCHAR) return;
\r
6977 history[histIn] = StrSave(cmd);
\r
6978 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6979 if (history[histIn] != NULL) {
\r
6980 free(history[histIn]);
\r
6982 history[histIn] = NULL;
\r
6988 PrevInHistory(char *cmd)
\r
6991 if (histP == histIn) {
\r
6992 if (history[histIn] != NULL) free(history[histIn]);
\r
6993 history[histIn] = StrSave(cmd);
\r
6995 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6996 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6998 return history[histP];
\r
7004 if (histP == histIn) return NULL;
\r
7005 histP = (histP + 1) % HISTORY_SIZE;
\r
7006 return history[histP];
\r
7010 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
7014 hmenu = LoadMenu(hInst, "TextMenu");
\r
7015 h = GetSubMenu(hmenu, 0);
\r
7017 if (strcmp(e->item, "-") == 0) {
\r
7018 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
7019 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
7020 int flags = MF_STRING, j = 0;
\r
7021 if (e->item[0] == '|') {
\r
7022 flags |= MF_MENUBARBREAK;
\r
7025 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
7026 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
7034 WNDPROC consoleTextWindowProc;
\r
7037 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
7039 char buf[MSG_SIZ], name[MSG_SIZ];
\r
7040 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7044 SetWindowText(hInput, command);
\r
7046 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
7048 sel.cpMin = 999999;
\r
7049 sel.cpMax = 999999;
\r
7050 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7055 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7056 if (sel.cpMin == sel.cpMax) {
\r
7057 /* Expand to surrounding word */
\r
7060 tr.chrg.cpMax = sel.cpMin;
\r
7061 tr.chrg.cpMin = --sel.cpMin;
\r
7062 if (sel.cpMin < 0) break;
\r
7063 tr.lpstrText = name;
\r
7064 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
7065 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
7069 tr.chrg.cpMin = sel.cpMax;
\r
7070 tr.chrg.cpMax = ++sel.cpMax;
\r
7071 tr.lpstrText = name;
\r
7072 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
7073 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
7076 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
7077 MessageBeep(MB_ICONEXCLAMATION);
\r
7081 tr.lpstrText = name;
\r
7082 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
7084 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
7085 MessageBeep(MB_ICONEXCLAMATION);
\r
7088 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
7091 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
7092 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
7093 SetWindowText(hInput, buf);
\r
7094 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
7096 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
7097 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
7098 SetWindowText(hInput, buf);
\r
7099 sel.cpMin = 999999;
\r
7100 sel.cpMax = 999999;
\r
7101 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7107 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7112 switch (message) {
\r
7114 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7115 if(wParam=='R') return 0;
\r
7118 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
7121 sel.cpMin = 999999;
\r
7122 sel.cpMax = 999999;
\r
7123 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7124 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
7129 if(wParam != '\022') {
\r
7130 if (wParam == '\t') {
\r
7131 if (GetKeyState(VK_SHIFT) < 0) {
\r
7133 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7134 if (buttonDesc[0].hwnd) {
\r
7135 SetFocus(buttonDesc[0].hwnd);
\r
7137 SetFocus(hwndMain);
\r
7141 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
7144 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7145 JAWS_DELETE( SetFocus(hInput); )
\r
7146 SendMessage(hInput, message, wParam, lParam);
\r
7149 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
7151 case WM_RBUTTONDOWN:
\r
7152 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
7153 /* Move selection here if it was empty */
\r
7155 pt.x = LOWORD(lParam);
\r
7156 pt.y = HIWORD(lParam);
\r
7157 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7158 if (sel.cpMin == sel.cpMax) {
\r
7159 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
7160 sel.cpMax = sel.cpMin;
\r
7161 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7163 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
7164 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
7166 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
7167 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7168 if (sel.cpMin == sel.cpMax) {
\r
7169 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7170 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
7172 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7173 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7175 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
7176 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
7177 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
7178 MenuPopup(hwnd, pt, hmenu, -1);
\r
7182 case WM_RBUTTONUP:
\r
7183 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7184 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7185 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7189 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7191 return SendMessage(hInput, message, wParam, lParam);
\r
7192 case WM_MBUTTONDOWN:
\r
7193 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7195 switch (LOWORD(wParam)) {
\r
7196 case IDM_QuickPaste:
\r
7198 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7199 if (sel.cpMin == sel.cpMax) {
\r
7200 MessageBeep(MB_ICONEXCLAMATION);
\r
7203 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7204 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7205 SendMessage(hInput, WM_PASTE, 0, 0);
\r
7210 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7213 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7216 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7220 int i = LOWORD(wParam) - IDM_CommandX;
\r
7221 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
7222 icsTextMenuEntry[i].command != NULL) {
\r
7223 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
7224 icsTextMenuEntry[i].getname,
\r
7225 icsTextMenuEntry[i].immediate);
\r
7233 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
7236 WNDPROC consoleInputWindowProc;
\r
7239 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7241 char buf[MSG_SIZ];
\r
7243 static BOOL sendNextChar = FALSE;
\r
7244 static BOOL quoteNextChar = FALSE;
\r
7245 InputSource *is = consoleInputSource;
\r
7249 switch (message) {
\r
7251 if (!appData.localLineEditing || sendNextChar) {
\r
7252 is->buf[0] = (CHAR) wParam;
\r
7254 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7255 sendNextChar = FALSE;
\r
7258 if (quoteNextChar) {
\r
7259 buf[0] = (char) wParam;
\r
7260 buf[1] = NULLCHAR;
\r
7261 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7262 quoteNextChar = FALSE;
\r
7266 case '\r': /* Enter key */
\r
7267 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7268 if (consoleEcho) SaveInHistory(is->buf);
\r
7269 is->buf[is->count++] = '\n';
\r
7270 is->buf[is->count] = NULLCHAR;
\r
7271 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7272 if (consoleEcho) {
\r
7273 ConsoleOutput(is->buf, is->count, TRUE);
\r
7274 } else if (appData.localLineEditing) {
\r
7275 ConsoleOutput("\n", 1, TRUE);
\r
7278 case '\033': /* Escape key */
\r
7279 SetWindowText(hwnd, "");
\r
7280 cf.cbSize = sizeof(CHARFORMAT);
\r
7281 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7282 if (consoleEcho) {
\r
7283 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7285 cf.crTextColor = COLOR_ECHOOFF;
\r
7287 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7288 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7290 case '\t': /* Tab key */
\r
7291 if (GetKeyState(VK_SHIFT) < 0) {
\r
7293 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7296 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7297 if (buttonDesc[0].hwnd) {
\r
7298 SetFocus(buttonDesc[0].hwnd);
\r
7300 SetFocus(hwndMain);
\r
7304 case '\023': /* Ctrl+S */
\r
7305 sendNextChar = TRUE;
\r
7307 case '\021': /* Ctrl+Q */
\r
7308 quoteNextChar = TRUE;
\r
7318 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7319 p = PrevInHistory(buf);
\r
7321 SetWindowText(hwnd, p);
\r
7322 sel.cpMin = 999999;
\r
7323 sel.cpMax = 999999;
\r
7324 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7329 p = NextInHistory();
\r
7331 SetWindowText(hwnd, p);
\r
7332 sel.cpMin = 999999;
\r
7333 sel.cpMax = 999999;
\r
7334 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7340 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7344 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7348 case WM_MBUTTONDOWN:
\r
7349 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7350 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7352 case WM_RBUTTONUP:
\r
7353 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7354 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7355 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7359 hmenu = LoadMenu(hInst, "InputMenu");
\r
7360 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7361 if (sel.cpMin == sel.cpMax) {
\r
7362 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7363 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7365 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7366 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7368 pt.x = LOWORD(lParam);
\r
7369 pt.y = HIWORD(lParam);
\r
7370 MenuPopup(hwnd, pt, hmenu, -1);
\r
7374 switch (LOWORD(wParam)) {
\r
7376 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7378 case IDM_SelectAll:
\r
7380 sel.cpMax = -1; /*999999?*/
\r
7381 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7384 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7387 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7390 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7395 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7398 #define CO_MAX 100000
\r
7399 #define CO_TRIM 1000
\r
7402 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7404 static SnapData sd;
\r
7405 HWND hText, hInput;
\r
7407 static int sizeX, sizeY;
\r
7408 int newSizeX, newSizeY;
\r
7412 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7413 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7415 switch (message) {
\r
7417 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7419 ENLINK *pLink = (ENLINK*)lParam;
\r
7420 if (pLink->msg == WM_LBUTTONUP)
\r
7424 tr.chrg = pLink->chrg;
\r
7425 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7426 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7427 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7428 free(tr.lpstrText);
\r
7432 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7433 hwndConsole = hDlg;
\r
7435 consoleTextWindowProc = (WNDPROC)
\r
7436 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7437 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7438 consoleInputWindowProc = (WNDPROC)
\r
7439 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7440 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7441 Colorize(ColorNormal, TRUE);
\r
7442 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7443 ChangedConsoleFont();
\r
7444 GetClientRect(hDlg, &rect);
\r
7445 sizeX = rect.right;
\r
7446 sizeY = rect.bottom;
\r
7447 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7448 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7449 WINDOWPLACEMENT wp;
\r
7450 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7451 wp.length = sizeof(WINDOWPLACEMENT);
\r
7453 wp.showCmd = SW_SHOW;
\r
7454 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7455 wp.rcNormalPosition.left = wpConsole.x;
\r
7456 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7457 wp.rcNormalPosition.top = wpConsole.y;
\r
7458 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7459 SetWindowPlacement(hDlg, &wp);
\r
7462 // [HGM] Chessknight's change 2004-07-13
\r
7463 else { /* Determine Defaults */
\r
7464 WINDOWPLACEMENT wp;
\r
7465 wpConsole.x = wpMain.width + 1;
\r
7466 wpConsole.y = wpMain.y;
\r
7467 wpConsole.width = screenWidth - wpMain.width;
\r
7468 wpConsole.height = wpMain.height;
\r
7469 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7470 wp.length = sizeof(WINDOWPLACEMENT);
\r
7472 wp.showCmd = SW_SHOW;
\r
7473 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7474 wp.rcNormalPosition.left = wpConsole.x;
\r
7475 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7476 wp.rcNormalPosition.top = wpConsole.y;
\r
7477 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7478 SetWindowPlacement(hDlg, &wp);
\r
7481 // Allow hText to highlight URLs and send notifications on them
\r
7482 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7483 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7484 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7485 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7499 if (IsIconic(hDlg)) break;
\r
7500 newSizeX = LOWORD(lParam);
\r
7501 newSizeY = HIWORD(lParam);
\r
7502 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7503 RECT rectText, rectInput;
\r
7505 int newTextHeight, newTextWidth;
\r
7506 GetWindowRect(hText, &rectText);
\r
7507 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7508 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7509 if (newTextHeight < 0) {
\r
7510 newSizeY += -newTextHeight;
\r
7511 newTextHeight = 0;
\r
7513 SetWindowPos(hText, NULL, 0, 0,
\r
7514 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7515 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7516 pt.x = rectInput.left;
\r
7517 pt.y = rectInput.top + newSizeY - sizeY;
\r
7518 ScreenToClient(hDlg, &pt);
\r
7519 SetWindowPos(hInput, NULL,
\r
7520 pt.x, pt.y, /* needs client coords */
\r
7521 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7522 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7528 case WM_GETMINMAXINFO:
\r
7529 /* Prevent resizing window too small */
\r
7530 mmi = (MINMAXINFO *) lParam;
\r
7531 mmi->ptMinTrackSize.x = 100;
\r
7532 mmi->ptMinTrackSize.y = 100;
\r
7535 /* [AS] Snapping */
\r
7536 case WM_ENTERSIZEMOVE:
\r
7537 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7540 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7543 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7545 case WM_EXITSIZEMOVE:
\r
7546 UpdateICSWidth(hText);
\r
7547 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7550 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7558 if (hwndConsole) return;
\r
7559 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7560 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7565 ConsoleOutput(char* data, int length, int forceVisible)
\r
7570 char buf[CO_MAX+1];
\r
7573 static int delayLF = 0;
\r
7574 CHARRANGE savesel, sel;
\r
7576 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7584 while (length--) {
\r
7592 } else if (*p == '\007') {
\r
7593 MyPlaySound(&sounds[(int)SoundBell]);
\r
7600 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7601 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7602 /* Save current selection */
\r
7603 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7604 exlen = GetWindowTextLength(hText);
\r
7605 /* Find out whether current end of text is visible */
\r
7606 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7607 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7608 /* Trim existing text if it's too long */
\r
7609 if (exlen + (q - buf) > CO_MAX) {
\r
7610 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7613 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7614 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7616 savesel.cpMin -= trim;
\r
7617 savesel.cpMax -= trim;
\r
7618 if (exlen < 0) exlen = 0;
\r
7619 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7620 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7622 /* Append the new text */
\r
7623 sel.cpMin = exlen;
\r
7624 sel.cpMax = exlen;
\r
7625 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7626 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7627 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7628 if (forceVisible || exlen == 0 ||
\r
7629 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7630 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7631 /* Scroll to make new end of text visible if old end of text
\r
7632 was visible or new text is an echo of user typein */
\r
7633 sel.cpMin = 9999999;
\r
7634 sel.cpMax = 9999999;
\r
7635 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7636 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7637 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7638 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7640 if (savesel.cpMax == exlen || forceVisible) {
\r
7641 /* Move insert point to new end of text if it was at the old
\r
7642 end of text or if the new text is an echo of user typein */
\r
7643 sel.cpMin = 9999999;
\r
7644 sel.cpMax = 9999999;
\r
7645 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7647 /* Restore previous selection */
\r
7648 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7650 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7657 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7661 COLORREF oldFg, oldBg;
\r
7665 if(copyNumber > 1)
\r
7666 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7668 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7669 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7670 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7673 rect.right = x + squareSize;
\r
7675 rect.bottom = y + squareSize;
\r
7678 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7679 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7680 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7681 &rect, str, strlen(str), NULL);
\r
7683 (void) SetTextColor(hdc, oldFg);
\r
7684 (void) SetBkColor(hdc, oldBg);
\r
7685 (void) SelectObject(hdc, oldFont);
\r
7689 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7690 RECT *rect, char *color, char *flagFell)
\r
7694 COLORREF oldFg, oldBg;
\r
7697 if (twoBoards && partnerUp) return;
\r
7698 if (appData.clockMode) {
\r
7699 if (tinyLayout == 2)
\r
7700 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7702 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7709 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7710 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7712 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7713 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7716 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7720 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7721 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7722 rect, str, strlen(str), NULL);
\r
7723 if(logoHeight > 0 && appData.clockMode) {
\r
7725 str += strlen(color)+2;
\r
7726 r.top = rect->top + logoHeight/2;
\r
7727 r.left = rect->left;
\r
7728 r.right = rect->right;
\r
7729 r.bottom = rect->bottom;
\r
7730 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7731 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7732 &r, str, strlen(str), NULL);
\r
7734 (void) SetTextColor(hdc, oldFg);
\r
7735 (void) SetBkColor(hdc, oldBg);
\r
7736 (void) SelectObject(hdc, oldFont);
\r
7741 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7747 if( count <= 0 ) {
\r
7748 if (appData.debugMode) {
\r
7749 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7752 return ERROR_INVALID_USER_BUFFER;
\r
7755 ResetEvent(ovl->hEvent);
\r
7756 ovl->Offset = ovl->OffsetHigh = 0;
\r
7757 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7761 err = GetLastError();
\r
7762 if (err == ERROR_IO_PENDING) {
\r
7763 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7767 err = GetLastError();
\r
7774 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7779 ResetEvent(ovl->hEvent);
\r
7780 ovl->Offset = ovl->OffsetHigh = 0;
\r
7781 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7785 err = GetLastError();
\r
7786 if (err == ERROR_IO_PENDING) {
\r
7787 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7791 err = GetLastError();
\r
7798 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7799 void CheckForInputBufferFull( InputSource * is )
\r
7801 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7802 /* Look for end of line */
\r
7803 char * p = is->buf;
\r
7805 while( p < is->next && *p != '\n' ) {
\r
7809 if( p >= is->next ) {
\r
7810 if (appData.debugMode) {
\r
7811 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7814 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7815 is->count = (DWORD) -1;
\r
7816 is->next = is->buf;
\r
7822 InputThread(LPVOID arg)
\r
7827 is = (InputSource *) arg;
\r
7828 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7829 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7830 while (is->hThread != NULL) {
\r
7831 is->error = DoReadFile(is->hFile, is->next,
\r
7832 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7833 &is->count, &ovl);
\r
7834 if (is->error == NO_ERROR) {
\r
7835 is->next += is->count;
\r
7837 if (is->error == ERROR_BROKEN_PIPE) {
\r
7838 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7841 is->count = (DWORD) -1;
\r
7842 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7847 CheckForInputBufferFull( is );
\r
7849 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7851 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7853 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7856 CloseHandle(ovl.hEvent);
\r
7857 CloseHandle(is->hFile);
\r
7859 if (appData.debugMode) {
\r
7860 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7867 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7869 NonOvlInputThread(LPVOID arg)
\r
7876 is = (InputSource *) arg;
\r
7877 while (is->hThread != NULL) {
\r
7878 is->error = ReadFile(is->hFile, is->next,
\r
7879 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7880 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7881 if (is->error == NO_ERROR) {
\r
7882 /* Change CRLF to LF */
\r
7883 if (is->next > is->buf) {
\r
7885 i = is->count + 1;
\r
7893 if (prev == '\r' && *p == '\n') {
\r
7905 if (is->error == ERROR_BROKEN_PIPE) {
\r
7906 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7909 is->count = (DWORD) -1;
\r
7913 CheckForInputBufferFull( is );
\r
7915 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7917 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7919 if (is->count < 0) break; /* Quit on error */
\r
7921 CloseHandle(is->hFile);
\r
7926 SocketInputThread(LPVOID arg)
\r
7930 is = (InputSource *) arg;
\r
7931 while (is->hThread != NULL) {
\r
7932 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7933 if ((int)is->count == SOCKET_ERROR) {
\r
7934 is->count = (DWORD) -1;
\r
7935 is->error = WSAGetLastError();
\r
7937 is->error = NO_ERROR;
\r
7938 is->next += is->count;
\r
7939 if (is->count == 0 && is->second == is) {
\r
7940 /* End of file on stderr; quit with no message */
\r
7944 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7946 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7948 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7954 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7958 is = (InputSource *) lParam;
\r
7959 if (is->lineByLine) {
\r
7960 /* Feed in lines one by one */
\r
7961 char *p = is->buf;
\r
7963 while (q < is->next) {
\r
7964 if (*q++ == '\n') {
\r
7965 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7970 /* Move any partial line to the start of the buffer */
\r
7972 while (p < is->next) {
\r
7977 if (is->error != NO_ERROR || is->count == 0) {
\r
7978 /* Notify backend of the error. Note: If there was a partial
\r
7979 line at the end, it is not flushed through. */
\r
7980 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7983 /* Feed in the whole chunk of input at once */
\r
7984 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7985 is->next = is->buf;
\r
7989 /*---------------------------------------------------------------------------*\
\r
7991 * Menu enables. Used when setting various modes.
\r
7993 \*---------------------------------------------------------------------------*/
\r
8001 GreyRevert(Boolean grey)
\r
8002 { // [HGM] vari: for retracting variations in local mode
\r
8003 HMENU hmenu = GetMenu(hwndMain);
\r
8004 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
8005 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
8009 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
8011 while (enab->item > 0) {
\r
8012 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
8017 Enables gnuEnables[] = {
\r
8018 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
8019 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
8020 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
8021 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
8022 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
8023 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
8024 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
8025 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
8026 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
8027 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
8028 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
8029 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
8030 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
8033 // Needed to switch from ncp to GNU mode on Engine Load
\r
8034 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
8035 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
8036 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
8037 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
8038 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
8039 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
8040 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
8041 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
8042 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
8043 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
8044 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
8045 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8046 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
8047 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
8051 Enables icsEnables[] = {
\r
8052 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
8053 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
8054 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8055 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8056 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8057 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8058 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
8059 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
8060 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
8061 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
8062 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8063 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
8064 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
8065 { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },
\r
8066 { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },
\r
8067 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
8068 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
8069 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
8070 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
8071 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
8076 Enables zippyEnables[] = {
\r
8077 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8078 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
8079 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
8080 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
8085 Enables ncpEnables[] = {
\r
8086 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
8087 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
8088 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8089 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8090 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8091 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8092 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
8093 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
8094 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
8095 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
8096 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
8097 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
8098 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8099 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
8100 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
8101 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
8102 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
8103 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
8104 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
8105 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
8106 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
8107 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
8111 Enables trainingOnEnables[] = {
\r
8112 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
8113 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
8114 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
8115 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
8116 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
8117 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
8118 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
8119 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8120 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
8124 Enables trainingOffEnables[] = {
\r
8125 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
8126 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
8127 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
8128 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
8129 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
8130 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
8131 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
8132 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8133 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
8137 /* These modify either ncpEnables or gnuEnables */
\r
8138 Enables cmailEnables[] = {
\r
8139 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
8140 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
8141 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
8142 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
8143 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
8144 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
8145 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
8149 Enables machineThinkingEnables[] = {
\r
8150 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8151 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
8152 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
8153 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8154 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
8155 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8156 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8157 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8158 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8159 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
8160 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8161 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8162 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8163 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8164 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
8165 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
8169 Enables userThinkingEnables[] = {
\r
8170 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8171 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
8172 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
8173 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8174 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
8175 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8176 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8177 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8178 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8179 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
8180 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
8181 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
8182 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
8183 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
8184 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
8185 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
8189 /*---------------------------------------------------------------------------*\
\r
8191 * Front-end interface functions exported by XBoard.
\r
8192 * Functions appear in same order as prototypes in frontend.h.
\r
8194 \*---------------------------------------------------------------------------*/
\r
8196 CheckMark(UINT item, int state)
\r
8198 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
8204 static UINT prevChecked = 0;
\r
8205 static int prevPausing = 0;
\r
8208 if (pausing != prevPausing) {
\r
8209 prevPausing = pausing;
\r
8210 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
8211 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
8212 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
8215 switch (gameMode) {
\r
8216 case BeginningOfGame:
\r
8217 if (appData.icsActive)
\r
8218 nowChecked = IDM_IcsClient;
\r
8219 else if (appData.noChessProgram)
\r
8220 nowChecked = IDM_EditGame;
\r
8222 nowChecked = IDM_MachineBlack;
\r
8224 case MachinePlaysBlack:
\r
8225 nowChecked = IDM_MachineBlack;
\r
8227 case MachinePlaysWhite:
\r
8228 nowChecked = IDM_MachineWhite;
\r
8230 case TwoMachinesPlay:
\r
8231 nowChecked = IDM_TwoMachines;
\r
8234 nowChecked = IDM_AnalysisMode;
\r
8237 nowChecked = IDM_AnalyzeFile;
\r
8240 nowChecked = IDM_EditGame;
\r
8242 case PlayFromGameFile:
\r
8243 nowChecked = IDM_LoadGame;
\r
8245 case EditPosition:
\r
8246 nowChecked = IDM_EditPosition;
\r
8249 nowChecked = IDM_Training;
\r
8251 case IcsPlayingWhite:
\r
8252 case IcsPlayingBlack:
\r
8253 case IcsObserving:
\r
8255 nowChecked = IDM_IcsClient;
\r
8262 if(prevChecked == IDM_TwoMachines) // [HGM] 'Machine Match' might have gotten disabled when stopping match
\r
8263 EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_ENABLED);
\r
8264 CheckMark(prevChecked, MF_UNCHECKED);
\r
8265 CheckMark(nowChecked, MF_CHECKED);
\r
8266 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8268 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8269 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8270 MF_BYCOMMAND|MF_ENABLED);
\r
8272 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8273 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8276 prevChecked = nowChecked;
\r
8278 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8279 if (appData.icsActive) {
\r
8280 if (appData.icsEngineAnalyze) {
\r
8281 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8283 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8286 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8292 HMENU hmenu = GetMenu(hwndMain);
\r
8293 SetMenuEnables(hmenu, icsEnables);
\r
8294 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8295 MF_BYCOMMAND|MF_ENABLED);
\r
8297 if (appData.zippyPlay) {
\r
8298 SetMenuEnables(hmenu, zippyEnables);
\r
8299 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8300 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8301 MF_BYCOMMAND|MF_ENABLED);
\r
8309 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8315 HMENU hmenu = GetMenu(hwndMain);
\r
8316 SetMenuEnables(hmenu, ncpEnables);
\r
8317 DrawMenuBar(hwndMain);
\r
8323 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8327 SetTrainingModeOn()
\r
8330 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8331 for (i = 0; i < N_BUTTONS; i++) {
\r
8332 if (buttonDesc[i].hwnd != NULL)
\r
8333 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8338 VOID SetTrainingModeOff()
\r
8341 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8342 for (i = 0; i < N_BUTTONS; i++) {
\r
8343 if (buttonDesc[i].hwnd != NULL)
\r
8344 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8350 SetUserThinkingEnables()
\r
8352 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8356 SetMachineThinkingEnables()
\r
8358 HMENU hMenu = GetMenu(hwndMain);
\r
8359 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8361 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8363 if (gameMode == MachinePlaysBlack) {
\r
8364 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8365 } else if (gameMode == MachinePlaysWhite) {
\r
8366 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8367 } else if (gameMode == TwoMachinesPlay) {
\r
8368 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8374 DisplayTitle(char *str)
\r
8376 char title[MSG_SIZ], *host;
\r
8377 if (str[0] != NULLCHAR) {
\r
8378 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8379 } else if (appData.icsActive) {
\r
8380 if (appData.icsCommPort[0] != NULLCHAR)
\r
8383 host = appData.icsHost;
\r
8384 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8385 } else if (appData.noChessProgram) {
\r
8386 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8388 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8389 strcat(title, ": ");
\r
8390 strcat(title, first.tidy);
\r
8392 SetWindowText(hwndMain, title);
\r
8397 DisplayMessage(char *str1, char *str2)
\r
8401 int remain = MESSAGE_TEXT_MAX - 1;
\r
8404 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8405 messageText[0] = NULLCHAR;
\r
8407 len = strlen(str1);
\r
8408 if (len > remain) len = remain;
\r
8409 strncpy(messageText, str1, len);
\r
8410 messageText[len] = NULLCHAR;
\r
8413 if (*str2 && remain >= 2) {
\r
8415 strcat(messageText, " ");
\r
8418 len = strlen(str2);
\r
8419 if (len > remain) len = remain;
\r
8420 strncat(messageText, str2, len);
\r
8422 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8423 safeStrCpy(lastMsg, messageText, MSG_SIZ);
\r
8425 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8429 hdc = GetDC(hwndMain);
\r
8430 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8431 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8432 &messageRect, messageText, strlen(messageText), NULL);
\r
8433 (void) SelectObject(hdc, oldFont);
\r
8434 (void) ReleaseDC(hwndMain, hdc);
\r
8438 DisplayError(char *str, int error)
\r
8440 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8444 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8446 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8447 NULL, error, LANG_NEUTRAL,
\r
8448 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8450 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8452 ErrorMap *em = errmap;
\r
8453 while (em->err != 0 && em->err != error) em++;
\r
8454 if (em->err != 0) {
\r
8455 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8457 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8462 ErrorPopUp(_("Error"), buf);
\r
8467 DisplayMoveError(char *str)
\r
8469 fromX = fromY = -1;
\r
8470 ClearHighlights();
\r
8471 DrawPosition(FALSE, NULL);
\r
8472 if (appData.popupMoveErrors) {
\r
8473 ErrorPopUp(_("Error"), str);
\r
8475 DisplayMessage(str, "");
\r
8476 moveErrorMessageUp = TRUE;
\r
8481 DisplayFatalError(char *str, int error, int exitStatus)
\r
8483 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8485 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8488 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8489 NULL, error, LANG_NEUTRAL,
\r
8490 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8492 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8494 ErrorMap *em = errmap;
\r
8495 while (em->err != 0 && em->err != error) em++;
\r
8496 if (em->err != 0) {
\r
8497 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8499 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8504 if (appData.debugMode) {
\r
8505 fprintf(debugFP, "%s: %s\n", label, str);
\r
8507 if (appData.popupExitMessage) {
\r
8508 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8509 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8511 ExitEvent(exitStatus);
\r
8516 DisplayInformation(char *str)
\r
8518 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8523 DisplayNote(char *str)
\r
8525 ErrorPopUp(_("Note"), str);
\r
8530 char *title, *question, *replyPrefix;
\r
8535 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8537 static QuestionParams *qp;
\r
8538 char reply[MSG_SIZ];
\r
8541 switch (message) {
\r
8542 case WM_INITDIALOG:
\r
8543 qp = (QuestionParams *) lParam;
\r
8544 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8545 Translate(hDlg, DLG_Question);
\r
8546 SetWindowText(hDlg, qp->title);
\r
8547 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8548 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8552 switch (LOWORD(wParam)) {
\r
8554 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8555 if (*reply) strcat(reply, " ");
\r
8556 len = strlen(reply);
\r
8557 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8558 strcat(reply, "\n");
\r
8559 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8560 EndDialog(hDlg, TRUE);
\r
8561 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8564 EndDialog(hDlg, FALSE);
\r
8575 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8577 QuestionParams qp;
\r
8581 qp.question = question;
\r
8582 qp.replyPrefix = replyPrefix;
\r
8584 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8585 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8586 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8587 FreeProcInstance(lpProc);
\r
8590 /* [AS] Pick FRC position */
\r
8591 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8593 static int * lpIndexFRC;
\r
8599 case WM_INITDIALOG:
\r
8600 lpIndexFRC = (int *) lParam;
\r
8602 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8603 Translate(hDlg, DLG_NewGameFRC);
\r
8605 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8606 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8607 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8608 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8613 switch( LOWORD(wParam) ) {
\r
8615 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8616 EndDialog( hDlg, 0 );
\r
8617 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8620 EndDialog( hDlg, 1 );
\r
8622 case IDC_NFG_Edit:
\r
8623 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8624 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8626 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8629 case IDC_NFG_Random:
\r
8630 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8631 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8644 int index = appData.defaultFrcPosition;
\r
8645 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8647 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8649 if( result == 0 ) {
\r
8650 appData.defaultFrcPosition = index;
\r
8656 /* [AS] Game list options. Refactored by HGM */
\r
8658 HWND gameListOptionsDialog;
\r
8660 // low-level front-end: clear text edit / list widget
\r
8665 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8668 // low-level front-end: clear text edit / list widget
\r
8670 GLT_DeSelectList()
\r
8672 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8675 // low-level front-end: append line to text edit / list widget
\r
8677 GLT_AddToList( char *name )
\r
8680 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8684 // low-level front-end: get line from text edit / list widget
\r
8686 GLT_GetFromList( int index, char *name )
\r
8689 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8695 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8697 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8698 int idx2 = idx1 + delta;
\r
8699 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8701 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8704 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8705 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8706 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8707 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8711 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8715 case WM_INITDIALOG:
\r
8716 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8718 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8719 Translate(hDlg, DLG_GameListOptions);
\r
8721 /* Initialize list */
\r
8722 GLT_TagsToList( lpUserGLT );
\r
8724 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8729 switch( LOWORD(wParam) ) {
\r
8732 EndDialog( hDlg, 0 );
\r
8735 EndDialog( hDlg, 1 );
\r
8738 case IDC_GLT_Default:
\r
8739 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8742 case IDC_GLT_Restore:
\r
8743 GLT_TagsToList( appData.gameListTags );
\r
8747 GLT_MoveSelection( hDlg, -1 );
\r
8750 case IDC_GLT_Down:
\r
8751 GLT_MoveSelection( hDlg, +1 );
\r
8761 int GameListOptions()
\r
8764 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8766 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8768 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8770 if( result == 0 ) {
\r
8771 char *oldTags = appData.gameListTags;
\r
8772 /* [AS] Memory leak here! */
\r
8773 appData.gameListTags = strdup( lpUserGLT );
\r
8774 if(strcmp(oldTags, appData.gameListTags)) // [HGM] redo Game List when we changed something
\r
8775 GameListToListBox(NULL, TRUE, ".", NULL, FALSE, FALSE); // "." as filter is kludge to select all
\r
8782 DisplayIcsInteractionTitle(char *str)
\r
8784 char consoleTitle[MSG_SIZ];
\r
8786 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8787 SetWindowText(hwndConsole, consoleTitle);
\r
8789 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
8790 char buf[MSG_SIZ], *p = buf, *q;
\r
8791 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
8793 q = strchr(p, ';');
\r
8795 if(*p) ChatPopUp(p);
\r
8799 SetActiveWindow(hwndMain);
\r
8803 DrawPosition(int fullRedraw, Board board)
\r
8805 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8808 void NotifyFrontendLogin()
\r
8811 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8817 fromX = fromY = -1;
\r
8818 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8819 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8820 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8821 dragInfo.lastpos = dragInfo.pos;
\r
8822 dragInfo.start.x = dragInfo.start.y = -1;
\r
8823 dragInfo.from = dragInfo.start;
\r
8825 DrawPosition(TRUE, NULL);
\r
8832 CommentPopUp(char *title, char *str)
\r
8834 HWND hwnd = GetActiveWindow();
\r
8835 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8837 SetActiveWindow(hwnd);
\r
8841 CommentPopDown(void)
\r
8843 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8844 if (commentDialog) {
\r
8845 ShowWindow(commentDialog, SW_HIDE);
\r
8847 commentUp = FALSE;
\r
8851 EditCommentPopUp(int index, char *title, char *str)
\r
8853 EitherCommentPopUp(index, title, str, TRUE);
\r
8860 MyPlaySound(&sounds[(int)SoundRoar]);
\r
8867 MyPlaySound(&sounds[(int)SoundMove]);
\r
8870 VOID PlayIcsWinSound()
\r
8872 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8875 VOID PlayIcsLossSound()
\r
8877 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8880 VOID PlayIcsDrawSound()
\r
8882 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8885 VOID PlayIcsUnfinishedSound()
\r
8887 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8893 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8899 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8907 consoleEcho = TRUE;
\r
8908 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8909 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8910 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8919 consoleEcho = FALSE;
\r
8920 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8921 /* This works OK: set text and background both to the same color */
\r
8923 cf.crTextColor = COLOR_ECHOOFF;
\r
8924 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8925 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8928 /* No Raw()...? */
\r
8930 void Colorize(ColorClass cc, int continuation)
\r
8932 currentColorClass = cc;
\r
8933 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8934 consoleCF.crTextColor = textAttribs[cc].color;
\r
8935 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8936 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8942 static char buf[MSG_SIZ];
\r
8943 DWORD bufsiz = MSG_SIZ;
\r
8945 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8946 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8948 if (!GetUserName(buf, &bufsiz)) {
\r
8949 /*DisplayError("Error getting user name", GetLastError());*/
\r
8950 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8958 static char buf[MSG_SIZ];
\r
8959 DWORD bufsiz = MSG_SIZ;
\r
8961 if (!GetComputerName(buf, &bufsiz)) {
\r
8962 /*DisplayError("Error getting host name", GetLastError());*/
\r
8963 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8970 ClockTimerRunning()
\r
8972 return clockTimerEvent != 0;
\r
8978 if (clockTimerEvent == 0) return FALSE;
\r
8979 KillTimer(hwndMain, clockTimerEvent);
\r
8980 clockTimerEvent = 0;
\r
8985 StartClockTimer(long millisec)
\r
8987 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8988 (UINT) millisec, NULL);
\r
8992 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8995 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8997 if(appData.noGUI) return;
\r
8998 hdc = GetDC(hwndMain);
\r
8999 if (!IsIconic(hwndMain)) {
\r
9000 DisplayAClock(hdc, timeRemaining, highlight,
\r
9001 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
9003 if (highlight && iconCurrent == iconBlack) {
\r
9004 iconCurrent = iconWhite;
\r
9005 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
9006 if (IsIconic(hwndMain)) {
\r
9007 DrawIcon(hdc, 2, 2, iconCurrent);
\r
9010 (void) ReleaseDC(hwndMain, hdc);
\r
9012 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
9016 DisplayBlackClock(long timeRemaining, int highlight)
\r
9019 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
9022 if(appData.noGUI) return;
\r
9023 hdc = GetDC(hwndMain);
\r
9024 if (!IsIconic(hwndMain)) {
\r
9025 DisplayAClock(hdc, timeRemaining, highlight,
\r
9026 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
9028 if (highlight && iconCurrent == iconWhite) {
\r
9029 iconCurrent = iconBlack;
\r
9030 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
9031 if (IsIconic(hwndMain)) {
\r
9032 DrawIcon(hdc, 2, 2, iconCurrent);
\r
9035 (void) ReleaseDC(hwndMain, hdc);
\r
9037 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
9042 LoadGameTimerRunning()
\r
9044 return loadGameTimerEvent != 0;
\r
9048 StopLoadGameTimer()
\r
9050 if (loadGameTimerEvent == 0) return FALSE;
\r
9051 KillTimer(hwndMain, loadGameTimerEvent);
\r
9052 loadGameTimerEvent = 0;
\r
9057 StartLoadGameTimer(long millisec)
\r
9059 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
9060 (UINT) millisec, NULL);
\r
9068 char fileTitle[MSG_SIZ];
\r
9070 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
9071 f = OpenFileDialog(hwndMain, "a", defName,
\r
9072 appData.oldSaveStyle ? "gam" : "pgn",
\r
9074 _("Save Game to File"), NULL, fileTitle, NULL);
\r
9076 SaveGame(f, 0, "");
\r
9083 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
9085 if (delayedTimerEvent != 0) {
\r
9086 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
9087 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
9089 KillTimer(hwndMain, delayedTimerEvent);
\r
9090 delayedTimerEvent = 0;
\r
9091 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
9092 delayedTimerCallback();
\r
9094 delayedTimerCallback = cb;
\r
9095 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
9096 (UINT) millisec, NULL);
\r
9099 DelayedEventCallback
\r
9102 if (delayedTimerEvent) {
\r
9103 return delayedTimerCallback;
\r
9110 CancelDelayedEvent()
\r
9112 if (delayedTimerEvent) {
\r
9113 KillTimer(hwndMain, delayedTimerEvent);
\r
9114 delayedTimerEvent = 0;
\r
9118 DWORD GetWin32Priority(int nice)
\r
9119 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
9121 REALTIME_PRIORITY_CLASS 0x00000100
\r
9122 HIGH_PRIORITY_CLASS 0x00000080
\r
9123 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
9124 NORMAL_PRIORITY_CLASS 0x00000020
\r
9125 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
9126 IDLE_PRIORITY_CLASS 0x00000040
\r
9128 if (nice < -15) return 0x00000080;
\r
9129 if (nice < 0) return 0x00008000;
\r
9130 if (nice == 0) return 0x00000020;
\r
9131 if (nice < 15) return 0x00004000;
\r
9132 return 0x00000040;
\r
9135 void RunCommand(char *cmdLine)
\r
9137 /* Now create the child process. */
\r
9138 STARTUPINFO siStartInfo;
\r
9139 PROCESS_INFORMATION piProcInfo;
\r
9141 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9142 siStartInfo.lpReserved = NULL;
\r
9143 siStartInfo.lpDesktop = NULL;
\r
9144 siStartInfo.lpTitle = NULL;
\r
9145 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9146 siStartInfo.cbReserved2 = 0;
\r
9147 siStartInfo.lpReserved2 = NULL;
\r
9148 siStartInfo.hStdInput = NULL;
\r
9149 siStartInfo.hStdOutput = NULL;
\r
9150 siStartInfo.hStdError = NULL;
\r
9152 CreateProcess(NULL,
\r
9153 cmdLine, /* command line */
\r
9154 NULL, /* process security attributes */
\r
9155 NULL, /* primary thread security attrs */
\r
9156 TRUE, /* handles are inherited */
\r
9157 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9158 NULL, /* use parent's environment */
\r
9160 &siStartInfo, /* STARTUPINFO pointer */
\r
9161 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9163 CloseHandle(piProcInfo.hThread);
\r
9166 /* Start a child process running the given program.
\r
9167 The process's standard output can be read from "from", and its
\r
9168 standard input can be written to "to".
\r
9169 Exit with fatal error if anything goes wrong.
\r
9170 Returns an opaque pointer that can be used to destroy the process
\r
9174 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
9176 #define BUFSIZE 4096
\r
9178 HANDLE hChildStdinRd, hChildStdinWr,
\r
9179 hChildStdoutRd, hChildStdoutWr;
\r
9180 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
9181 SECURITY_ATTRIBUTES saAttr;
\r
9183 PROCESS_INFORMATION piProcInfo;
\r
9184 STARTUPINFO siStartInfo;
\r
9186 char buf[MSG_SIZ];
\r
9189 if (appData.debugMode) {
\r
9190 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
9195 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
9196 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
9197 saAttr.bInheritHandle = TRUE;
\r
9198 saAttr.lpSecurityDescriptor = NULL;
\r
9201 * The steps for redirecting child's STDOUT:
\r
9202 * 1. Create anonymous pipe to be STDOUT for child.
\r
9203 * 2. Create a noninheritable duplicate of read handle,
\r
9204 * and close the inheritable read handle.
\r
9207 /* Create a pipe for the child's STDOUT. */
\r
9208 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
9209 return GetLastError();
\r
9212 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
9213 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
9214 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
9215 FALSE, /* not inherited */
\r
9216 DUPLICATE_SAME_ACCESS);
\r
9218 return GetLastError();
\r
9220 CloseHandle(hChildStdoutRd);
\r
9223 * The steps for redirecting child's STDIN:
\r
9224 * 1. Create anonymous pipe to be STDIN for child.
\r
9225 * 2. Create a noninheritable duplicate of write handle,
\r
9226 * and close the inheritable write handle.
\r
9229 /* Create a pipe for the child's STDIN. */
\r
9230 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
9231 return GetLastError();
\r
9234 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
9235 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
9236 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
9237 FALSE, /* not inherited */
\r
9238 DUPLICATE_SAME_ACCESS);
\r
9240 return GetLastError();
\r
9242 CloseHandle(hChildStdinWr);
\r
9244 /* Arrange to (1) look in dir for the child .exe file, and
\r
9245 * (2) have dir be the child's working directory. Interpret
\r
9246 * dir relative to the directory WinBoard loaded from. */
\r
9247 GetCurrentDirectory(MSG_SIZ, buf);
\r
9248 SetCurrentDirectory(installDir);
\r
9249 SetCurrentDirectory(dir);
\r
9251 /* Now create the child process. */
\r
9253 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9254 siStartInfo.lpReserved = NULL;
\r
9255 siStartInfo.lpDesktop = NULL;
\r
9256 siStartInfo.lpTitle = NULL;
\r
9257 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9258 siStartInfo.cbReserved2 = 0;
\r
9259 siStartInfo.lpReserved2 = NULL;
\r
9260 siStartInfo.hStdInput = hChildStdinRd;
\r
9261 siStartInfo.hStdOutput = hChildStdoutWr;
\r
9262 siStartInfo.hStdError = hChildStdoutWr;
\r
9264 fSuccess = CreateProcess(NULL,
\r
9265 cmdLine, /* command line */
\r
9266 NULL, /* process security attributes */
\r
9267 NULL, /* primary thread security attrs */
\r
9268 TRUE, /* handles are inherited */
\r
9269 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9270 NULL, /* use parent's environment */
\r
9272 &siStartInfo, /* STARTUPINFO pointer */
\r
9273 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9275 err = GetLastError();
\r
9276 SetCurrentDirectory(buf); /* return to prev directory */
\r
9281 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
9282 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
9283 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
9286 /* Close the handles we don't need in the parent */
\r
9287 CloseHandle(piProcInfo.hThread);
\r
9288 CloseHandle(hChildStdinRd);
\r
9289 CloseHandle(hChildStdoutWr);
\r
9291 /* Prepare return value */
\r
9292 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9293 cp->kind = CPReal;
\r
9294 cp->hProcess = piProcInfo.hProcess;
\r
9295 cp->pid = piProcInfo.dwProcessId;
\r
9296 cp->hFrom = hChildStdoutRdDup;
\r
9297 cp->hTo = hChildStdinWrDup;
\r
9299 *pr = (void *) cp;
\r
9301 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
9302 2000 where engines sometimes don't see the initial command(s)
\r
9303 from WinBoard and hang. I don't understand how that can happen,
\r
9304 but the Sleep is harmless, so I've put it in. Others have also
\r
9305 reported what may be the same problem, so hopefully this will fix
\r
9306 it for them too. */
\r
9314 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
9316 ChildProc *cp; int result;
\r
9318 cp = (ChildProc *) pr;
\r
9319 if (cp == NULL) return;
\r
9321 switch (cp->kind) {
\r
9323 /* TerminateProcess is considered harmful, so... */
\r
9324 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9325 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9326 /* The following doesn't work because the chess program
\r
9327 doesn't "have the same console" as WinBoard. Maybe
\r
9328 we could arrange for this even though neither WinBoard
\r
9329 nor the chess program uses a console for stdio? */
\r
9330 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9332 /* [AS] Special termination modes for misbehaving programs... */
\r
9333 if( signal & 8 ) {
\r
9334 result = TerminateProcess( cp->hProcess, 0 );
\r
9336 if ( appData.debugMode) {
\r
9337 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9340 else if( signal & 4 ) {
\r
9341 DWORD dw = WaitForSingleObject( cp->hProcess, appData.delayAfterQuit*1000 + 50 ); // Wait 3 seconds at most
\r
9343 if( dw != WAIT_OBJECT_0 ) {
\r
9344 result = TerminateProcess( cp->hProcess, 0 );
\r
9346 if ( appData.debugMode) {
\r
9347 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9353 CloseHandle(cp->hProcess);
\r
9357 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9361 closesocket(cp->sock);
\r
9366 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9367 closesocket(cp->sock);
\r
9368 closesocket(cp->sock2);
\r
9376 InterruptChildProcess(ProcRef pr)
\r
9380 cp = (ChildProc *) pr;
\r
9381 if (cp == NULL) return;
\r
9382 switch (cp->kind) {
\r
9384 /* The following doesn't work because the chess program
\r
9385 doesn't "have the same console" as WinBoard. Maybe
\r
9386 we could arrange for this even though neither WinBoard
\r
9387 nor the chess program uses a console for stdio */
\r
9388 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9393 /* Can't interrupt */
\r
9397 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9404 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9406 char cmdLine[MSG_SIZ];
\r
9408 if (port[0] == NULLCHAR) {
\r
9409 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9411 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9413 return StartChildProcess(cmdLine, "", pr);
\r
9417 /* Code to open TCP sockets */
\r
9420 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9426 struct sockaddr_in sa, mysa;
\r
9427 struct hostent FAR *hp;
\r
9428 unsigned short uport;
\r
9429 WORD wVersionRequested;
\r
9432 /* Initialize socket DLL */
\r
9433 wVersionRequested = MAKEWORD(1, 1);
\r
9434 err = WSAStartup(wVersionRequested, &wsaData);
\r
9435 if (err != 0) return err;
\r
9438 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9439 err = WSAGetLastError();
\r
9444 /* Bind local address using (mostly) don't-care values.
\r
9446 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9447 mysa.sin_family = AF_INET;
\r
9448 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9449 uport = (unsigned short) 0;
\r
9450 mysa.sin_port = htons(uport);
\r
9451 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9452 == SOCKET_ERROR) {
\r
9453 err = WSAGetLastError();
\r
9458 /* Resolve remote host name */
\r
9459 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9460 if (!(hp = gethostbyname(host))) {
\r
9461 unsigned int b0, b1, b2, b3;
\r
9463 err = WSAGetLastError();
\r
9465 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9466 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9467 hp->h_addrtype = AF_INET;
\r
9469 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9470 hp->h_addr_list[0] = (char *) malloc(4);
\r
9471 hp->h_addr_list[0][0] = (char) b0;
\r
9472 hp->h_addr_list[0][1] = (char) b1;
\r
9473 hp->h_addr_list[0][2] = (char) b2;
\r
9474 hp->h_addr_list[0][3] = (char) b3;
\r
9480 sa.sin_family = hp->h_addrtype;
\r
9481 uport = (unsigned short) atoi(port);
\r
9482 sa.sin_port = htons(uport);
\r
9483 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9485 /* Make connection */
\r
9486 if (connect(s, (struct sockaddr *) &sa,
\r
9487 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9488 err = WSAGetLastError();
\r
9493 /* Prepare return value */
\r
9494 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9495 cp->kind = CPSock;
\r
9497 *pr = (ProcRef *) cp;
\r
9503 OpenCommPort(char *name, ProcRef *pr)
\r
9508 char fullname[MSG_SIZ];
\r
9510 if (*name != '\\')
\r
9511 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9513 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9515 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9516 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9517 if (h == (HANDLE) -1) {
\r
9518 return GetLastError();
\r
9522 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9524 /* Accumulate characters until a 100ms pause, then parse */
\r
9525 ct.ReadIntervalTimeout = 100;
\r
9526 ct.ReadTotalTimeoutMultiplier = 0;
\r
9527 ct.ReadTotalTimeoutConstant = 0;
\r
9528 ct.WriteTotalTimeoutMultiplier = 0;
\r
9529 ct.WriteTotalTimeoutConstant = 0;
\r
9530 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9532 /* Prepare return value */
\r
9533 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9534 cp->kind = CPComm;
\r
9537 *pr = (ProcRef *) cp;
\r
9543 OpenLoopback(ProcRef *pr)
\r
9545 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9551 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9556 struct sockaddr_in sa, mysa;
\r
9557 struct hostent FAR *hp;
\r
9558 unsigned short uport;
\r
9559 WORD wVersionRequested;
\r
9562 char stderrPortStr[MSG_SIZ];
\r
9564 /* Initialize socket DLL */
\r
9565 wVersionRequested = MAKEWORD(1, 1);
\r
9566 err = WSAStartup(wVersionRequested, &wsaData);
\r
9567 if (err != 0) return err;
\r
9569 /* Resolve remote host name */
\r
9570 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9571 if (!(hp = gethostbyname(host))) {
\r
9572 unsigned int b0, b1, b2, b3;
\r
9574 err = WSAGetLastError();
\r
9576 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9577 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9578 hp->h_addrtype = AF_INET;
\r
9580 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9581 hp->h_addr_list[0] = (char *) malloc(4);
\r
9582 hp->h_addr_list[0][0] = (char) b0;
\r
9583 hp->h_addr_list[0][1] = (char) b1;
\r
9584 hp->h_addr_list[0][2] = (char) b2;
\r
9585 hp->h_addr_list[0][3] = (char) b3;
\r
9591 sa.sin_family = hp->h_addrtype;
\r
9592 uport = (unsigned short) 514;
\r
9593 sa.sin_port = htons(uport);
\r
9594 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9596 /* Bind local socket to unused "privileged" port address
\r
9598 s = INVALID_SOCKET;
\r
9599 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9600 mysa.sin_family = AF_INET;
\r
9601 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9602 for (fromPort = 1023;; fromPort--) {
\r
9603 if (fromPort < 0) {
\r
9605 return WSAEADDRINUSE;
\r
9607 if (s == INVALID_SOCKET) {
\r
9608 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9609 err = WSAGetLastError();
\r
9614 uport = (unsigned short) fromPort;
\r
9615 mysa.sin_port = htons(uport);
\r
9616 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9617 == SOCKET_ERROR) {
\r
9618 err = WSAGetLastError();
\r
9619 if (err == WSAEADDRINUSE) continue;
\r
9623 if (connect(s, (struct sockaddr *) &sa,
\r
9624 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9625 err = WSAGetLastError();
\r
9626 if (err == WSAEADDRINUSE) {
\r
9637 /* Bind stderr local socket to unused "privileged" port address
\r
9639 s2 = INVALID_SOCKET;
\r
9640 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9641 mysa.sin_family = AF_INET;
\r
9642 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9643 for (fromPort = 1023;; fromPort--) {
\r
9644 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9645 if (fromPort < 0) {
\r
9646 (void) closesocket(s);
\r
9648 return WSAEADDRINUSE;
\r
9650 if (s2 == INVALID_SOCKET) {
\r
9651 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9652 err = WSAGetLastError();
\r
9658 uport = (unsigned short) fromPort;
\r
9659 mysa.sin_port = htons(uport);
\r
9660 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9661 == SOCKET_ERROR) {
\r
9662 err = WSAGetLastError();
\r
9663 if (err == WSAEADDRINUSE) continue;
\r
9664 (void) closesocket(s);
\r
9668 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9669 err = WSAGetLastError();
\r
9670 if (err == WSAEADDRINUSE) {
\r
9672 s2 = INVALID_SOCKET;
\r
9675 (void) closesocket(s);
\r
9676 (void) closesocket(s2);
\r
9682 prevStderrPort = fromPort; // remember port used
\r
9683 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9685 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9686 err = WSAGetLastError();
\r
9687 (void) closesocket(s);
\r
9688 (void) closesocket(s2);
\r
9693 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9694 err = WSAGetLastError();
\r
9695 (void) closesocket(s);
\r
9696 (void) closesocket(s2);
\r
9700 if (*user == NULLCHAR) user = UserName();
\r
9701 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9702 err = WSAGetLastError();
\r
9703 (void) closesocket(s);
\r
9704 (void) closesocket(s2);
\r
9708 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9709 err = WSAGetLastError();
\r
9710 (void) closesocket(s);
\r
9711 (void) closesocket(s2);
\r
9716 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9717 err = WSAGetLastError();
\r
9718 (void) closesocket(s);
\r
9719 (void) closesocket(s2);
\r
9723 (void) closesocket(s2); /* Stop listening */
\r
9725 /* Prepare return value */
\r
9726 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9727 cp->kind = CPRcmd;
\r
9730 *pr = (ProcRef *) cp;
\r
9737 AddInputSource(ProcRef pr, int lineByLine,
\r
9738 InputCallback func, VOIDSTAR closure)
\r
9740 InputSource *is, *is2 = NULL;
\r
9741 ChildProc *cp = (ChildProc *) pr;
\r
9743 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9744 is->lineByLine = lineByLine;
\r
9746 is->closure = closure;
\r
9747 is->second = NULL;
\r
9748 is->next = is->buf;
\r
9749 if (pr == NoProc) {
\r
9750 is->kind = CPReal;
\r
9751 consoleInputSource = is;
\r
9753 is->kind = cp->kind;
\r
9755 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9756 we create all threads suspended so that the is->hThread variable can be
\r
9757 safely assigned, then let the threads start with ResumeThread.
\r
9759 switch (cp->kind) {
\r
9761 is->hFile = cp->hFrom;
\r
9762 cp->hFrom = NULL; /* now owned by InputThread */
\r
9764 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9765 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9769 is->hFile = cp->hFrom;
\r
9770 cp->hFrom = NULL; /* now owned by InputThread */
\r
9772 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9773 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9777 is->sock = cp->sock;
\r
9779 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9780 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9784 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9786 is->sock = cp->sock;
\r
9788 is2->sock = cp->sock2;
\r
9789 is2->second = is2;
\r
9791 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9792 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9794 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9795 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9799 if( is->hThread != NULL ) {
\r
9800 ResumeThread( is->hThread );
\r
9803 if( is2 != NULL && is2->hThread != NULL ) {
\r
9804 ResumeThread( is2->hThread );
\r
9808 return (InputSourceRef) is;
\r
9812 RemoveInputSource(InputSourceRef isr)
\r
9816 is = (InputSource *) isr;
\r
9817 is->hThread = NULL; /* tell thread to stop */
\r
9818 CloseHandle(is->hThread);
\r
9819 if (is->second != NULL) {
\r
9820 is->second->hThread = NULL;
\r
9821 CloseHandle(is->second->hThread);
\r
9825 int no_wrap(char *message, int count)
\r
9827 ConsoleOutput(message, count, FALSE);
\r
9832 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9835 int outCount = SOCKET_ERROR;
\r
9836 ChildProc *cp = (ChildProc *) pr;
\r
9837 static OVERLAPPED ovl;
\r
9839 static int line = 0;
\r
9843 if (appData.noJoin || !appData.useInternalWrap)
\r
9844 return no_wrap(message, count);
\r
9847 int width = get_term_width();
\r
9848 int len = wrap(NULL, message, count, width, &line);
\r
9849 char *msg = malloc(len);
\r
9853 return no_wrap(message, count);
\r
9856 dbgchk = wrap(msg, message, count, width, &line);
\r
9857 if (dbgchk != len && appData.debugMode)
\r
9858 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9859 ConsoleOutput(msg, len, FALSE);
\r
9866 if (ovl.hEvent == NULL) {
\r
9867 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9869 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9871 switch (cp->kind) {
\r
9874 outCount = send(cp->sock, message, count, 0);
\r
9875 if (outCount == SOCKET_ERROR) {
\r
9876 *outError = WSAGetLastError();
\r
9878 *outError = NO_ERROR;
\r
9883 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9884 &dOutCount, NULL)) {
\r
9885 *outError = NO_ERROR;
\r
9886 outCount = (int) dOutCount;
\r
9888 *outError = GetLastError();
\r
9893 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9894 &dOutCount, &ovl);
\r
9895 if (*outError == NO_ERROR) {
\r
9896 outCount = (int) dOutCount;
\r
9906 if(n != 0) Sleep(n);
\r
9910 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9913 /* Ignore delay, not implemented for WinBoard */
\r
9914 return OutputToProcess(pr, message, count, outError);
\r
9919 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9920 char *buf, int count, int error)
\r
9922 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9925 /* see wgamelist.c for Game List functions */
\r
9926 /* see wedittags.c for Edit Tags functions */
\r
9933 char buf[MSG_SIZ];
\r
9936 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9937 f = fopen(buf, "r");
\r
9939 ProcessICSInitScript(f);
\r
9949 StartAnalysisClock()
\r
9951 if (analysisTimerEvent) return;
\r
9952 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9953 (UINT) 2000, NULL);
\r
9957 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9959 highlightInfo.sq[0].x = fromX;
\r
9960 highlightInfo.sq[0].y = fromY;
\r
9961 highlightInfo.sq[1].x = toX;
\r
9962 highlightInfo.sq[1].y = toY;
\r
9968 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9969 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9973 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9975 premoveHighlightInfo.sq[0].x = fromX;
\r
9976 premoveHighlightInfo.sq[0].y = fromY;
\r
9977 premoveHighlightInfo.sq[1].x = toX;
\r
9978 premoveHighlightInfo.sq[1].y = toY;
\r
9982 ClearPremoveHighlights()
\r
9984 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9985 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9989 ShutDownFrontEnd()
\r
9991 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9992 DeleteClipboardTempFiles();
\r
9998 if (IsIconic(hwndMain))
\r
9999 ShowWindow(hwndMain, SW_RESTORE);
\r
10001 SetActiveWindow(hwndMain);
\r
10005 * Prototypes for animation support routines
\r
10007 static void ScreenSquare(int column, int row, POINT * pt);
\r
10008 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
10009 POINT frames[], int * nFrames);
\r
10012 #define kFactor 4
\r
10015 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
10016 { // [HGM] atomic: animate blast wave
\r
10019 explodeInfo.fromX = fromX;
\r
10020 explodeInfo.fromY = fromY;
\r
10021 explodeInfo.toX = toX;
\r
10022 explodeInfo.toY = toY;
\r
10023 for(i=1; i<4*kFactor; i++) {
\r
10024 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
10025 DrawPosition(FALSE, board);
\r
10026 Sleep(appData.animSpeed);
\r
10028 explodeInfo.radius = 0;
\r
10029 DrawPosition(TRUE, board);
\r
10033 AnimateMove(board, fromX, fromY, toX, toY)
\r
10040 ChessSquare piece;
\r
10041 int x = toX, y = toY;
\r
10042 POINT start, finish, mid;
\r
10043 POINT frames[kFactor * 2 + 1];
\r
10046 if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();
\r
10048 if (!appData.animate) return;
\r
10049 if (doingSizing) return;
\r
10050 if (fromY < 0 || fromX < 0) return;
\r
10051 piece = board[fromY][fromX];
\r
10052 if (piece >= EmptySquare) return;
\r
10054 if(killX >= 0) toX = killX, toY = killY; // [HGM] lion: first to kill square
\r
10058 ScreenSquare(fromX, fromY, &start);
\r
10059 ScreenSquare(toX, toY, &finish);
\r
10061 /* All moves except knight jumps move in straight line */
\r
10062 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
10063 mid.x = start.x + (finish.x - start.x) / 2;
\r
10064 mid.y = start.y + (finish.y - start.y) / 2;
\r
10066 /* Knight: make straight movement then diagonal */
\r
10067 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
10068 mid.x = start.x + (finish.x - start.x) / 2;
\r
10072 mid.y = start.y + (finish.y - start.y) / 2;
\r
10076 /* Don't use as many frames for very short moves */
\r
10077 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
10078 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
10080 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
10082 animInfo.from.x = fromX;
\r
10083 animInfo.from.y = fromY;
\r
10084 animInfo.to.x = toX;
\r
10085 animInfo.to.y = toY;
\r
10086 animInfo.lastpos = start;
\r
10087 animInfo.piece = piece;
\r
10088 for (n = 0; n < nFrames; n++) {
\r
10089 animInfo.pos = frames[n];
\r
10090 DrawPosition(FALSE, NULL);
\r
10091 animInfo.lastpos = animInfo.pos;
\r
10092 Sleep(appData.animSpeed);
\r
10094 animInfo.pos = finish;
\r
10095 DrawPosition(FALSE, NULL);
\r
10097 if(toX != x || toY != y) { fromX = toX; fromY = toY; toX = x; toY = y; goto again; } // second leg
\r
10099 animInfo.piece = EmptySquare;
\r
10100 Explode(board, fromX, fromY, toX, toY);
\r
10103 /* Convert board position to corner of screen rect and color */
\r
10106 ScreenSquare(column, row, pt)
\r
10107 int column; int row; POINT * pt;
\r
10110 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
10111 pt->y = lineGap + row * (squareSize + lineGap) + border;
\r
10113 pt->x = lineGap + column * (squareSize + lineGap) + border;
\r
10114 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
10118 /* Generate a series of frame coords from start->mid->finish.
\r
10119 The movement rate doubles until the half way point is
\r
10120 reached, then halves back down to the final destination,
\r
10121 which gives a nice slow in/out effect. The algorithmn
\r
10122 may seem to generate too many intermediates for short
\r
10123 moves, but remember that the purpose is to attract the
\r
10124 viewers attention to the piece about to be moved and
\r
10125 then to where it ends up. Too few frames would be less
\r
10129 Tween(start, mid, finish, factor, frames, nFrames)
\r
10130 POINT * start; POINT * mid;
\r
10131 POINT * finish; int factor;
\r
10132 POINT frames[]; int * nFrames;
\r
10134 int n, fraction = 1, count = 0;
\r
10136 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
10137 for (n = 0; n < factor; n++)
\r
10139 for (n = 0; n < factor; n++) {
\r
10140 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
10141 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
10143 fraction = fraction / 2;
\r
10147 frames[count] = *mid;
\r
10150 /* Slow out, stepping 1/2, then 1/4, ... */
\r
10152 for (n = 0; n < factor; n++) {
\r
10153 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
10154 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
10156 fraction = fraction * 2;
\r
10158 *nFrames = count;
\r
10162 SettingsPopUp(ChessProgramState *cps)
\r
10163 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
10164 EngineOptionsPopup(savedHwnd, cps);
\r
10167 int flock(int fid, int code)
\r
10169 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
10171 ov.hEvent = NULL;
\r
10173 ov.OffsetHigh = 0;
\r
10175 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
10177 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
10178 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
10179 default: return -1;
\r
10188 static char col[8][20];
\r
10189 COLORREF color = *(COLORREF *) colorVariable[n];
\r
10191 snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
10196 ActivateTheme (int new)
\r
10197 { // Redo initialization of features depending on options that can occur in themes
\r
10199 if(new) InitDrawingColors();
\r
10200 fontBitmapSquareSize = 0; // request creation of new font pieces
\r
10201 InitDrawingSizes(boardSize, 0);
\r
10202 InvalidateRect(hwndMain, NULL, TRUE);
\r