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
4109 HBITMAP src = bufferBitmap, obmp; HDC tmp = CreateCompatibleDC(hdc);
\r
4111 bufferBitmap = CreateCompatibleBitmap(hdc, boardRect.right-boardRect.left, Rect.bottom-Rect.top-2*OUTER_MARGIN);
\r
4112 obmp = SelectObject(tmp, bufferBitmap);
\r
4113 BitBlt(tmp, 0, 0, boardRect.right - boardRect.left, Rect.bottom - Rect.top - 2*OUTER_MARGIN,
\r
4114 tmphdc, boardRect.left, OUTER_MARGIN, SRCCOPY);
\r
4115 GetObject(bufferBitmap, sizeof(b), &b);
\r
4116 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\r
4117 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
4118 bih.biWidth = b.bmWidth;
\r
4119 bih.biHeight = b.bmHeight;
\r
4121 bih.biBitCount = b.bmBitsPixel;
\r
4122 bih.biCompression = 0;
\r
4123 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
4124 bih.biXPelsPerMeter = 0;
\r
4125 bih.biYPelsPerMeter = 0;
\r
4126 bih.biClrUsed = 0;
\r
4127 bih.biClrImportant = 0;
\r
4128 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
4129 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
4130 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
4131 // fprintf(diagFile, "%8x\n", (int) pData);
\r
4133 wb = b.bmWidthBytes;
\r
4135 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
4136 int k = ((int*) pData)[i];
\r
4137 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4138 if(j >= 16) break;
\r
4140 if(j >= nrColors) nrColors = j+1;
\r
4142 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
4144 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
4145 for(w=0; w<(wb>>2); w+=2) {
\r
4146 int k = ((int*) pData)[(wb*i>>2) + w];
\r
4147 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4148 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
4149 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
4150 pData[p++] = m | j<<4;
\r
4152 while(p&3) pData[p++] = 0;
\r
4155 wb = ((wb+31)>>5)<<2;
\r
4157 // write BITMAPFILEHEADER
\r
4158 fprintf(diagFile, "BM");
\r
4159 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
4160 fputDW(diagFile, 0);
\r
4161 fputDW(diagFile, 0x36 + (fac?64:0));
\r
4162 // write BITMAPINFOHEADER
\r
4163 fputDW(diagFile, 40);
\r
4164 fputDW(diagFile, b.bmWidth);
\r
4165 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
4166 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
4167 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
4168 fputDW(diagFile, 0);
\r
4169 fputDW(diagFile, 0);
\r
4170 fputDW(diagFile, 0);
\r
4171 fputDW(diagFile, 0);
\r
4172 fputDW(diagFile, 0);
\r
4173 fputDW(diagFile, 0);
\r
4174 // write color table
\r
4176 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
4177 // write bitmap data
\r
4178 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
4179 fputc(pData[i], diagFile);
\r
4182 DeleteObject(bufferBitmap); bufferBitmap = src;
\r
4183 SelectObject(tmp, obmp);
\r
4187 SelectObject(tmphdc, oldBitmap);
\r
4189 /* Massive cleanup */
\r
4190 for (x = 0; x < num_clips; x++)
\r
4191 DeleteObject(clips[x]);
\r
4194 DeleteObject(bufferBitmap);
\r
4197 ReleaseDC(hwndMain, hdc);
\r
4199 if (lastDrawnFlipView != flipView && nr == 0) {
\r
4201 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
4203 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
4206 /* CopyBoard(lastDrawn, board);*/
\r
4207 lastDrawnHighlight = highlightInfo;
\r
4208 lastDrawnPremove = premoveHighlightInfo;
\r
4209 lastDrawnFlipView = flipView;
\r
4210 lastDrawnValid[nr] = 1;
\r
4213 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
4218 saveDiagFlag = 1; diagFile = f;
\r
4219 HDCDrawPosition(NULL, TRUE, NULL);
\r
4227 /*---------------------------------------------------------------------------*\
\r
4228 | CLIENT PAINT PROCEDURE
\r
4229 | This is the main event-handler for the WM_PAINT message.
\r
4231 \*---------------------------------------------------------------------------*/
\r
4233 PaintProc(HWND hwnd)
\r
4239 if((hdc = BeginPaint(hwnd, &ps))) {
\r
4240 if (IsIconic(hwnd)) {
\r
4241 DrawIcon(hdc, 2, 2, iconCurrent);
\r
4243 if (!appData.monoMode) {
\r
4244 SelectPalette(hdc, hPal, FALSE);
\r
4245 RealizePalette(hdc);
\r
4247 HDCDrawPosition(hdc, 1, NULL);
\r
4248 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
4249 flipView = !flipView; partnerUp = !partnerUp;
\r
4250 HDCDrawPosition(hdc, 1, NULL);
\r
4251 flipView = !flipView; partnerUp = !partnerUp;
\r
4254 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
4255 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
4256 ETO_CLIPPED|ETO_OPAQUE,
\r
4257 &messageRect, messageText, strlen(messageText), NULL);
\r
4258 SelectObject(hdc, oldFont);
\r
4259 DisplayBothClocks();
\r
4262 EndPaint(hwnd,&ps);
\r
4270 * If the user selects on a border boundary, return -1; if off the board,
\r
4271 * return -2. Otherwise map the event coordinate to the square.
\r
4272 * The offset boardRect.left or boardRect.top must already have been
\r
4273 * subtracted from x.
\r
4275 int EventToSquare(x, limit)
\r
4280 if (x < lineGap + border)
\r
4282 x -= lineGap + border;
\r
4283 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4285 x /= (squareSize + lineGap);
\r
4297 DropEnable dropEnables[] = {
\r
4298 { 'P', DP_Pawn, N_("Pawn") },
\r
4299 { 'N', DP_Knight, N_("Knight") },
\r
4300 { 'B', DP_Bishop, N_("Bishop") },
\r
4301 { 'R', DP_Rook, N_("Rook") },
\r
4302 { 'Q', DP_Queen, N_("Queen") },
\r
4306 SetupDropMenu(HMENU hmenu)
\r
4308 int i, count, enable;
\r
4310 extern char white_holding[], black_holding[];
\r
4311 char item[MSG_SIZ];
\r
4313 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4314 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4315 dropEnables[i].piece);
\r
4317 while (p && *p++ == dropEnables[i].piece) count++;
\r
4318 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4319 enable = count > 0 || !appData.testLegality
\r
4320 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4321 && !appData.icsActive);
\r
4322 ModifyMenu(hmenu, dropEnables[i].command,
\r
4323 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4324 dropEnables[i].command, item);
\r
4328 void DragPieceBegin(int x, int y, Boolean instantly)
\r
4330 dragInfo.lastpos.x = boardRect.left + x;
\r
4331 dragInfo.lastpos.y = boardRect.top + y;
\r
4332 if(instantly) dragInfo.pos = dragInfo.lastpos;
\r
4333 dragInfo.from.x = fromX;
\r
4334 dragInfo.from.y = fromY;
\r
4335 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4336 dragInfo.start = dragInfo.from;
\r
4337 SetCapture(hwndMain);
\r
4340 void DragPieceEnd(int x, int y)
\r
4343 dragInfo.start.x = dragInfo.start.y = -1;
\r
4344 dragInfo.from = dragInfo.start;
\r
4345 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4348 void ChangeDragPiece(ChessSquare piece)
\r
4350 dragInfo.piece = piece;
\r
4353 /* Event handler for mouse messages */
\r
4355 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4359 static int recursive = 0;
\r
4361 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4364 if (message == WM_MBUTTONUP) {
\r
4365 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4366 to the middle button: we simulate pressing the left button too!
\r
4368 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4369 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4375 pt.x = LOWORD(lParam);
\r
4376 pt.y = HIWORD(lParam);
\r
4377 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4378 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4379 if (!flipView && y >= 0) {
\r
4380 y = BOARD_HEIGHT - 1 - y;
\r
4382 if (flipView && x >= 0) {
\r
4383 x = BOARD_WIDTH - 1 - x;
\r
4386 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4387 controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status
\r
4389 switch (message) {
\r
4390 case WM_LBUTTONDOWN:
\r
4391 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4392 ClockClick(flipClock); break;
\r
4393 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4394 ClockClick(!flipClock); break;
\r
4396 if(dragging) { // [HGM] lion: don't destroy dragging info if we are already dragging
\r
4397 dragInfo.start.x = dragInfo.start.y = -1;
\r
4398 dragInfo.from = dragInfo.start;
\r
4400 if(fromX == -1 && frozen) { // not sure where this is for
\r
4401 fromX = fromY = -1;
\r
4402 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4405 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4406 DrawPosition(TRUE, NULL);
\r
4409 case WM_LBUTTONUP:
\r
4410 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4411 DrawPosition(TRUE, NULL);
\r
4414 case WM_MOUSEMOVE:
\r
4415 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4416 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4417 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4418 if ((appData.animateDragging || appData.highlightDragging)
\r
4419 && (wParam & MK_LBUTTON || dragging == 2)
\r
4420 && dragInfo.from.x >= 0)
\r
4422 BOOL full_repaint = FALSE;
\r
4424 if (appData.animateDragging) {
\r
4425 dragInfo.pos = pt;
\r
4427 if (appData.highlightDragging) {
\r
4428 HoverEvent(highlightInfo.sq[1].x, highlightInfo.sq[1].y, x, y);
\r
4429 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4430 full_repaint = TRUE;
\r
4434 DrawPosition( full_repaint, NULL);
\r
4436 dragInfo.lastpos = dragInfo.pos;
\r
4440 case WM_MOUSEWHEEL: // [DM]
\r
4441 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4442 /* Mouse Wheel is being rolled forward
\r
4443 * Play moves forward
\r
4445 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4446 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4447 /* Mouse Wheel is being rolled backward
\r
4448 * Play moves backward
\r
4450 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4451 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4455 case WM_MBUTTONUP:
\r
4456 case WM_RBUTTONUP:
\r
4458 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4461 case WM_MBUTTONDOWN:
\r
4462 case WM_RBUTTONDOWN:
\r
4465 fromX = fromY = -1;
\r
4466 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4467 dragInfo.start.x = dragInfo.start.y = -1;
\r
4468 dragInfo.from = dragInfo.start;
\r
4469 dragInfo.lastpos = dragInfo.pos;
\r
4470 if (appData.highlightDragging) {
\r
4471 ClearHighlights();
\r
4474 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4475 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4476 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4477 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4478 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4482 DrawPosition(TRUE, NULL);
\r
4484 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4487 if (message == WM_MBUTTONDOWN) {
\r
4488 buttonCount = 3; /* even if system didn't think so */
\r
4489 if (wParam & MK_SHIFT)
\r
4490 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4492 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4493 } else { /* message == WM_RBUTTONDOWN */
\r
4494 /* Just have one menu, on the right button. Windows users don't
\r
4495 think to try the middle one, and sometimes other software steals
\r
4496 it, or it doesn't really exist. */
\r
4497 if(gameInfo.variant != VariantShogi)
\r
4498 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4500 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4504 SetCapture(hwndMain);
\r
4507 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4508 SetupDropMenu(hmenu);
\r
4509 MenuPopup(hwnd, pt, hmenu, -1);
\r
4519 /* Preprocess messages for buttons in main window */
\r
4521 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4523 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4526 for (i=0; i<N_BUTTONS; i++) {
\r
4527 if (buttonDesc[i].id == id) break;
\r
4529 if (i == N_BUTTONS) return 0;
\r
4530 switch (message) {
\r
4535 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4536 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4543 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4546 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4547 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4548 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4549 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4551 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4553 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4554 TypeInEvent((char)wParam);
\r
4560 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4563 static int promoStyle;
\r
4565 /* Process messages for Promotion dialog box */
\r
4567 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4572 switch (message) {
\r
4574 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4575 /* Center the dialog over the application window */
\r
4576 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4577 Translate(hDlg, DLG_PromotionKing);
\r
4578 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4579 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4580 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4581 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4582 SW_SHOW : SW_HIDE);
\r
4583 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4584 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4585 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4586 PieceToChar(WhiteAngel) != '~') ||
\r
4587 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4588 PieceToChar(BlackAngel) != '~') ) ?
\r
4589 SW_SHOW : SW_HIDE);
\r
4590 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4591 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4592 PieceToChar(WhiteMarshall) != '~') ||
\r
4593 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4594 PieceToChar(BlackMarshall) != '~') ) ?
\r
4595 SW_SHOW : SW_HIDE);
\r
4596 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4597 ShowWindow(GetDlgItem(hDlg, PB_Rook), !promoStyle ? SW_SHOW : SW_HIDE);
\r
4598 ShowWindow(GetDlgItem(hDlg, PB_Bishop), !promoStyle ? SW_SHOW : SW_HIDE);
\r
4600 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4601 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4602 SetWindowText(hDlg, "Promote?");
\r
4604 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4605 gameInfo.variant == VariantSuper ?
\r
4606 SW_SHOW : SW_HIDE);
\r
4609 case WM_COMMAND: /* message: received a command */
\r
4610 switch (LOWORD(wParam)) {
\r
4612 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4613 ClearHighlights();
\r
4614 DrawPosition(FALSE, NULL);
\r
4617 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4620 promoChar = promoStyle ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4623 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4624 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4627 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4628 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4630 case PB_Chancellor:
\r
4631 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4633 case PB_Archbishop:
\r
4634 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4637 promoChar = gameInfo.variant == VariantShogi ? '=' : promoStyle ? NULLCHAR :
\r
4638 ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight));
\r
4643 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4644 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4645 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4646 fromX = fromY = -1;
\r
4647 if (!appData.highlightLastMove) {
\r
4648 ClearHighlights();
\r
4649 DrawPosition(FALSE, NULL);
\r
4656 /* Pop up promotion dialog */
\r
4658 PromotionPopup(HWND hwnd)
\r
4662 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4663 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4664 hwnd, (DLGPROC)lpProc);
\r
4665 FreeProcInstance(lpProc);
\r
4669 PromotionPopUp(char choice)
\r
4671 promoStyle = (choice == '+' || IS_SHOGI(gameInfo.variant));
\r
4672 DrawPosition(TRUE, NULL);
\r
4673 PromotionPopup(hwndMain);
\r
4677 LoadGameDialog(HWND hwnd, char* title)
\r
4681 char fileTitle[MSG_SIZ];
\r
4682 f = OpenFileDialog(hwnd, "rb", "",
\r
4683 appData.oldSaveStyle ? "gam" : "pgn",
\r
4685 title, &number, fileTitle, NULL);
\r
4687 cmailMsgLoaded = FALSE;
\r
4688 if (number == 0) {
\r
4689 int error = GameListBuild(f);
\r
4691 DisplayError(_("Cannot build game list"), error);
\r
4692 } else if (!ListEmpty(&gameList) &&
\r
4693 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4694 GameListPopUp(f, fileTitle);
\r
4697 GameListDestroy();
\r
4700 LoadGame(f, number, fileTitle, FALSE);
\r
4704 int get_term_width()
\r
4709 HFONT hfont, hold_font;
\r
4714 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4718 // get the text metrics
\r
4719 hdc = GetDC(hText);
\r
4720 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4721 if (consoleCF.dwEffects & CFE_BOLD)
\r
4722 lf.lfWeight = FW_BOLD;
\r
4723 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4724 lf.lfItalic = TRUE;
\r
4725 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4726 lf.lfStrikeOut = TRUE;
\r
4727 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4728 lf.lfUnderline = TRUE;
\r
4729 hfont = CreateFontIndirect(&lf);
\r
4730 hold_font = SelectObject(hdc, hfont);
\r
4731 GetTextMetrics(hdc, &tm);
\r
4732 SelectObject(hdc, hold_font);
\r
4733 DeleteObject(hfont);
\r
4734 ReleaseDC(hText, hdc);
\r
4736 // get the rectangle
\r
4737 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4739 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4742 void UpdateICSWidth(HWND hText)
\r
4744 LONG old_width, new_width;
\r
4746 new_width = get_term_width(hText, FALSE);
\r
4747 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4748 if (new_width != old_width)
\r
4750 ics_update_width(new_width);
\r
4751 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4756 ChangedConsoleFont()
\r
4759 CHARRANGE tmpsel, sel;
\r
4760 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4761 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4762 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4765 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4766 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4767 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4768 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4769 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4770 * size. This was undocumented in the version of MSVC++ that I had
\r
4771 * when I wrote the code, but is apparently documented now.
\r
4773 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4774 cfmt.bCharSet = f->lf.lfCharSet;
\r
4775 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4776 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4777 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4778 /* Why are the following seemingly needed too? */
\r
4779 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4780 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4781 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4783 tmpsel.cpMax = -1; /*999999?*/
\r
4784 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4785 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4786 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4787 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4789 paraf.cbSize = sizeof(paraf);
\r
4790 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4791 paraf.dxStartIndent = 0;
\r
4792 paraf.dxOffset = WRAP_INDENT;
\r
4793 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4794 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4795 UpdateICSWidth(hText);
\r
4798 /*---------------------------------------------------------------------------*\
\r
4800 * Window Proc for main window
\r
4802 \*---------------------------------------------------------------------------*/
\r
4804 /* Process messages for main window, etc. */
\r
4806 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4813 char fileTitle[MSG_SIZ];
\r
4814 static SnapData sd;
\r
4815 static int peek=0;
\r
4817 switch (message) {
\r
4819 case WM_PAINT: /* message: repaint portion of window */
\r
4823 case WM_ERASEBKGND:
\r
4824 if (IsIconic(hwnd)) {
\r
4825 /* Cheat; change the message */
\r
4826 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4828 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4832 case WM_LBUTTONDOWN:
\r
4833 case WM_MBUTTONDOWN:
\r
4834 case WM_RBUTTONDOWN:
\r
4835 case WM_LBUTTONUP:
\r
4836 case WM_MBUTTONUP:
\r
4837 case WM_RBUTTONUP:
\r
4838 case WM_MOUSEMOVE:
\r
4839 case WM_MOUSEWHEEL:
\r
4840 MouseEvent(hwnd, message, wParam, lParam);
\r
4844 if((char)wParam == '\b') {
\r
4845 ForwardEvent(); peek = 0;
\r
4848 JAWS_KBUP_NAVIGATION
\r
4853 if((char)wParam == '\b') {
\r
4854 if(!peek) BackwardEvent(), peek = 1;
\r
4857 JAWS_KBDOWN_NAVIGATION
\r
4863 JAWS_ALT_INTERCEPT
\r
4865 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4866 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4867 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4868 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4870 SendMessage(h, message, wParam, lParam);
\r
4871 } else if(lParam != KF_REPEAT) {
\r
4872 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4873 TypeInEvent((char)wParam);
\r
4874 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4875 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4880 case WM_PALETTECHANGED:
\r
4881 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4883 HDC hdc = GetDC(hwndMain);
\r
4884 SelectPalette(hdc, hPal, TRUE);
\r
4885 nnew = RealizePalette(hdc);
\r
4887 paletteChanged = TRUE;
\r
4889 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4891 ReleaseDC(hwnd, hdc);
\r
4895 case WM_QUERYNEWPALETTE:
\r
4896 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4898 HDC hdc = GetDC(hwndMain);
\r
4899 paletteChanged = FALSE;
\r
4900 SelectPalette(hdc, hPal, FALSE);
\r
4901 nnew = RealizePalette(hdc);
\r
4903 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4905 ReleaseDC(hwnd, hdc);
\r
4910 case WM_COMMAND: /* message: command from application menu */
\r
4911 wmId = LOWORD(wParam);
\r
4916 SAY("new game enter a move to play against the computer with white");
\r
4919 case IDM_NewGameFRC:
\r
4920 if( NewGameFRC() == 0 ) {
\r
4925 case IDM_NewVariant:
\r
4926 NewVariantPopup(hwnd);
\r
4929 case IDM_LoadGame:
\r
4930 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4933 case IDM_LoadNextGame:
\r
4937 case IDM_LoadPrevGame:
\r
4941 case IDM_ReloadGame:
\r
4945 case IDM_LoadPosition:
\r
4946 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4947 Reset(FALSE, TRUE);
\r
4950 f = OpenFileDialog(hwnd, "rb", "",
\r
4951 appData.oldSaveStyle ? "pos" : "fen",
\r
4953 _("Load Position from File"), &number, fileTitle, NULL);
\r
4955 LoadPosition(f, number, fileTitle);
\r
4959 case IDM_LoadNextPosition:
\r
4960 ReloadPosition(1);
\r
4963 case IDM_LoadPrevPosition:
\r
4964 ReloadPosition(-1);
\r
4967 case IDM_ReloadPosition:
\r
4968 ReloadPosition(0);
\r
4971 case IDM_SaveGame:
\r
4972 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4973 f = OpenFileDialog(hwnd, "a", defName,
\r
4974 appData.oldSaveStyle ? "gam" : "pgn",
\r
4976 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4978 SaveGame(f, 0, "");
\r
4982 case IDM_SavePosition:
\r
4983 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4984 f = OpenFileDialog(hwnd, "a", defName,
\r
4985 appData.oldSaveStyle ? "pos" : "fen",
\r
4987 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4989 SavePosition(f, 0, "");
\r
4993 case IDM_SaveDiagram:
\r
4994 defName = "diagram";
\r
4995 f = OpenFileDialog(hwnd, "wb", defName,
\r
4998 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
5004 case IDM_SaveSelected:
\r
5005 f = OpenFileDialog(hwnd, "a", "",
\r
5008 _("Save Game to File"), NULL, fileTitle, NULL);
\r
5010 SaveSelected(f, 0, "");
\r
5014 case IDM_CreateBook:
\r
5015 CreateBookEvent();
\r
5018 case IDM_CopyGame:
\r
5019 CopyGameToClipboard();
\r
5022 case IDM_PasteGame:
\r
5023 PasteGameFromClipboard();
\r
5026 case IDM_CopyGameListToClipboard:
\r
5027 CopyGameListToClipboard();
\r
5030 /* [AS] Autodetect FEN or PGN data */
\r
5031 case IDM_PasteAny:
\r
5032 PasteGameOrFENFromClipboard();
\r
5035 /* [AS] Move history */
\r
5036 case IDM_ShowMoveHistory:
\r
5037 if( MoveHistoryIsUp() ) {
\r
5038 MoveHistoryPopDown();
\r
5041 MoveHistoryPopUp();
\r
5045 /* [AS] Eval graph */
\r
5046 case IDM_ShowEvalGraph:
\r
5047 if( EvalGraphIsUp() ) {
\r
5048 EvalGraphPopDown();
\r
5052 SetFocus(hwndMain);
\r
5056 /* [AS] Engine output */
\r
5057 case IDM_ShowEngineOutput:
\r
5058 if( EngineOutputIsUp() ) {
\r
5059 EngineOutputPopDown();
\r
5062 EngineOutputPopUp();
\r
5066 /* [AS] User adjudication */
\r
5067 case IDM_UserAdjudication_White:
\r
5068 UserAdjudicationEvent( +1 );
\r
5071 case IDM_UserAdjudication_Black:
\r
5072 UserAdjudicationEvent( -1 );
\r
5075 case IDM_UserAdjudication_Draw:
\r
5076 UserAdjudicationEvent( 0 );
\r
5079 /* [AS] Game list options dialog */
\r
5080 case IDM_GameListOptions:
\r
5081 GameListOptions();
\r
5088 case IDM_CopyPosition:
\r
5089 CopyFENToClipboard();
\r
5092 case IDM_PastePosition:
\r
5093 PasteFENFromClipboard();
\r
5096 case IDM_MailMove:
\r
5100 case IDM_ReloadCMailMsg:
\r
5101 Reset(TRUE, TRUE);
\r
5102 ReloadCmailMsgEvent(FALSE);
\r
5105 case IDM_Minimize:
\r
5106 ShowWindow(hwnd, SW_MINIMIZE);
\r
5113 case IDM_MachineWhite:
\r
5114 MachineWhiteEvent();
\r
5116 * refresh the tags dialog only if it's visible
\r
5118 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
5120 tags = PGNTags(&gameInfo);
\r
5121 TagsPopUp(tags, CmailMsg());
\r
5124 SAY("computer starts playing white");
\r
5127 case IDM_MachineBlack:
\r
5128 MachineBlackEvent();
\r
5130 * refresh the tags dialog only if it's visible
\r
5132 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
5134 tags = PGNTags(&gameInfo);
\r
5135 TagsPopUp(tags, CmailMsg());
\r
5138 SAY("computer starts playing black");
\r
5141 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
5142 if(matchMode) EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_GRAYED);
\r
5143 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
5146 case IDM_TwoMachines:
\r
5147 TwoMachinesEvent();
\r
5150 * refresh the tags dialog only if it's visible
\r
5152 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
5154 tags = PGNTags(&gameInfo);
\r
5155 TagsPopUp(tags, CmailMsg());
\r
5158 SAY("computer starts playing both sides");
\r
5161 case IDM_AnalysisMode:
\r
5162 if(AnalyzeModeEvent()) {
\r
5163 SAY("analyzing current position");
\r
5167 case IDM_AnalyzeFile:
\r
5168 AnalyzeFileEvent();
\r
5171 case IDM_IcsClient:
\r
5175 case IDM_EditGame:
\r
5176 case IDM_EditGame2:
\r
5181 case IDM_EditPosition:
\r
5182 case IDM_EditPosition2:
\r
5183 EditPositionEvent();
\r
5184 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
5187 case IDM_Training:
\r
5191 case IDM_ShowGameList:
\r
5192 ShowGameListProc();
\r
5195 case IDM_EditProgs1:
\r
5196 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
5199 case IDM_LoadProg1:
\r
5200 LoadEnginePopUp(hwndMain, 0);
\r
5203 case IDM_LoadProg2:
\r
5204 LoadEnginePopUp(hwndMain, 1);
\r
5207 case IDM_EditServers:
\r
5208 EditTagsPopUp(icsNames, &icsNames);
\r
5211 case IDM_EditTags:
\r
5216 case IDM_EditBook:
\r
5220 case IDM_EditComment:
\r
5222 if (commentUp && editComment) {
\r
5225 EditCommentEvent();
\r
5246 case IDM_CallFlag:
\r
5266 case IDM_StopObserving:
\r
5267 StopObservingEvent();
\r
5270 case IDM_StopExamining:
\r
5271 StopExaminingEvent();
\r
5275 UploadGameEvent();
\r
5278 case IDM_TypeInMove:
\r
5279 TypeInEvent('\000');
\r
5282 case IDM_TypeInName:
\r
5283 PopUpNameDialog('\000');
\r
5286 case IDM_Backward:
\r
5288 SetFocus(hwndMain);
\r
5295 SetFocus(hwndMain);
\r
5300 SetFocus(hwndMain);
\r
5305 SetFocus(hwndMain);
\r
5308 case OPT_GameListNext: // [HGM] forward these two accelerators to Game List
\r
5309 case OPT_GameListPrev:
\r
5310 if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);
\r
5314 RevertEvent(FALSE);
\r
5317 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5318 RevertEvent(TRUE);
\r
5321 case IDM_TruncateGame:
\r
5322 TruncateGameEvent();
\r
5329 case IDM_RetractMove:
\r
5330 RetractMoveEvent();
\r
5333 case IDM_FlipView:
\r
5334 flipView = !flipView;
\r
5335 DrawPosition(FALSE, NULL);
\r
5338 case IDM_FlipClock:
\r
5339 flipClock = !flipClock;
\r
5340 DisplayBothClocks();
\r
5344 case IDM_MuteSounds:
\r
5345 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5346 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5347 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5350 case IDM_GeneralOptions:
\r
5351 GeneralOptionsPopup(hwnd);
\r
5352 DrawPosition(TRUE, NULL);
\r
5355 case IDM_BoardOptions:
\r
5356 BoardOptionsPopup(hwnd);
\r
5359 case IDM_ThemeOptions:
\r
5360 ThemeOptionsPopup(hwnd);
\r
5363 case IDM_EnginePlayOptions:
\r
5364 EnginePlayOptionsPopup(hwnd);
\r
5367 case IDM_Engine1Options:
\r
5368 EngineOptionsPopup(hwnd, &first);
\r
5371 case IDM_Engine2Options:
\r
5373 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5374 EngineOptionsPopup(hwnd, &second);
\r
5377 case IDM_OptionsUCI:
\r
5378 UciOptionsPopup(hwnd);
\r
5382 TourneyPopup(hwnd);
\r
5385 case IDM_IcsOptions:
\r
5386 IcsOptionsPopup(hwnd);
\r
5390 FontsOptionsPopup(hwnd);
\r
5394 SoundOptionsPopup(hwnd);
\r
5397 case IDM_CommPort:
\r
5398 CommPortOptionsPopup(hwnd);
\r
5401 case IDM_LoadOptions:
\r
5402 LoadOptionsPopup(hwnd);
\r
5405 case IDM_SaveOptions:
\r
5406 SaveOptionsPopup(hwnd);
\r
5409 case IDM_TimeControl:
\r
5410 TimeControlOptionsPopup(hwnd);
\r
5413 case IDM_SaveSettings:
\r
5414 SaveSettings(settingsFileName);
\r
5417 case IDM_SaveSettingsOnExit:
\r
5418 saveSettingsOnExit = !saveSettingsOnExit;
\r
5419 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5420 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5421 MF_CHECKED : MF_UNCHECKED));
\r
5432 case IDM_AboutGame:
\r
5437 appData.debugMode = !appData.debugMode;
\r
5438 if (appData.debugMode) {
\r
5439 char dir[MSG_SIZ];
\r
5440 GetCurrentDirectory(MSG_SIZ, dir);
\r
5441 SetCurrentDirectory(installDir);
\r
5442 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5443 SetCurrentDirectory(dir);
\r
5444 setbuf(debugFP, NULL);
\r
5451 case IDM_HELPCONTENTS:
\r
5452 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5453 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5454 MessageBox (GetFocus(),
\r
5455 _("Unable to activate help"),
\r
5456 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5460 case IDM_HELPSEARCH:
\r
5461 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5462 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5463 MessageBox (GetFocus(),
\r
5464 _("Unable to activate help"),
\r
5465 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5469 case IDM_HELPHELP:
\r
5470 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5471 MessageBox (GetFocus(),
\r
5472 _("Unable to activate help"),
\r
5473 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5478 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5480 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5481 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5482 FreeProcInstance(lpProc);
\r
5485 case IDM_DirectCommand1:
\r
5486 AskQuestionEvent(_("Direct Command"),
\r
5487 _("Send to chess program:"), "", "1");
\r
5489 case IDM_DirectCommand2:
\r
5490 AskQuestionEvent(_("Direct Command"),
\r
5491 _("Send to second chess program:"), "", "2");
\r
5494 case EP_WhitePawn:
\r
5495 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5496 fromX = fromY = -1;
\r
5499 case EP_WhiteKnight:
\r
5500 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5501 fromX = fromY = -1;
\r
5504 case EP_WhiteBishop:
\r
5505 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5506 fromX = fromY = -1;
\r
5509 case EP_WhiteRook:
\r
5510 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5511 fromX = fromY = -1;
\r
5514 case EP_WhiteQueen:
\r
5515 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5516 fromX = fromY = -1;
\r
5519 case EP_WhiteFerz:
\r
5520 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5521 fromX = fromY = -1;
\r
5524 case EP_WhiteWazir:
\r
5525 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5526 fromX = fromY = -1;
\r
5529 case EP_WhiteAlfil:
\r
5530 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5531 fromX = fromY = -1;
\r
5534 case EP_WhiteCannon:
\r
5535 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5536 fromX = fromY = -1;
\r
5539 case EP_WhiteCardinal:
\r
5540 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5541 fromX = fromY = -1;
\r
5544 case EP_WhiteMarshall:
\r
5545 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5546 fromX = fromY = -1;
\r
5549 case EP_WhiteKing:
\r
5550 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5551 fromX = fromY = -1;
\r
5554 case EP_BlackPawn:
\r
5555 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5556 fromX = fromY = -1;
\r
5559 case EP_BlackKnight:
\r
5560 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5561 fromX = fromY = -1;
\r
5564 case EP_BlackBishop:
\r
5565 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5566 fromX = fromY = -1;
\r
5569 case EP_BlackRook:
\r
5570 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5571 fromX = fromY = -1;
\r
5574 case EP_BlackQueen:
\r
5575 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5576 fromX = fromY = -1;
\r
5579 case EP_BlackFerz:
\r
5580 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5581 fromX = fromY = -1;
\r
5584 case EP_BlackWazir:
\r
5585 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5586 fromX = fromY = -1;
\r
5589 case EP_BlackAlfil:
\r
5590 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5591 fromX = fromY = -1;
\r
5594 case EP_BlackCannon:
\r
5595 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5596 fromX = fromY = -1;
\r
5599 case EP_BlackCardinal:
\r
5600 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5601 fromX = fromY = -1;
\r
5604 case EP_BlackMarshall:
\r
5605 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5606 fromX = fromY = -1;
\r
5609 case EP_BlackKing:
\r
5610 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5611 fromX = fromY = -1;
\r
5614 case EP_EmptySquare:
\r
5615 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5616 fromX = fromY = -1;
\r
5619 case EP_ClearBoard:
\r
5620 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5621 fromX = fromY = -1;
\r
5625 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5626 fromX = fromY = -1;
\r
5630 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5631 fromX = fromY = -1;
\r
5635 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5636 fromX = fromY = -1;
\r
5640 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5641 fromX = fromY = -1;
\r
5645 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5646 fromX = fromY = -1;
\r
5650 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5651 fromX = fromY = -1;
\r
5655 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5656 fromX = fromY = -1;
\r
5660 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5661 fromX = fromY = -1;
\r
5665 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5666 fromX = fromY = -1;
\r
5670 barbaric = 0; appData.language = "";
\r
5671 TranslateMenus(0);
\r
5672 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5673 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5674 lastChecked = wmId;
\r
5678 if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)
\r
5679 RecentEngineEvent(wmId - IDM_RecentEngines);
\r
5681 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5682 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5683 TranslateMenus(0);
\r
5684 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5685 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5686 lastChecked = wmId;
\r
5689 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5695 case CLOCK_TIMER_ID:
\r
5696 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5697 clockTimerEvent = 0;
\r
5698 DecrementClocks(); /* call into back end */
\r
5700 case LOAD_GAME_TIMER_ID:
\r
5701 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5702 loadGameTimerEvent = 0;
\r
5703 AutoPlayGameLoop(); /* call into back end */
\r
5705 case ANALYSIS_TIMER_ID:
\r
5706 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5707 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5708 AnalysisPeriodicEvent(0);
\r
5710 KillTimer(hwnd, analysisTimerEvent);
\r
5711 analysisTimerEvent = 0;
\r
5714 case DELAYED_TIMER_ID:
\r
5715 KillTimer(hwnd, delayedTimerEvent);
\r
5716 delayedTimerEvent = 0;
\r
5717 delayedTimerCallback();
\r
5722 case WM_USER_Input:
\r
5723 InputEvent(hwnd, message, wParam, lParam);
\r
5726 /* [AS] Also move "attached" child windows */
\r
5727 case WM_WINDOWPOSCHANGING:
\r
5729 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5730 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5732 if( ((lpwp->flags & SWP_NOMOVE) == 0) /*&& ((lpwp->flags & SWP_NOSIZE) != 0)*/ ) { // [HGM] in Win8 size always accompanies move?
\r
5733 /* Window is moving */
\r
5736 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5737 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5738 rcMain.right = wpMain.x + wpMain.width;
\r
5739 rcMain.top = wpMain.y;
\r
5740 rcMain.bottom = wpMain.y + wpMain.height;
\r
5742 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5743 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5744 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5745 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5746 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5747 wpMain.x = lpwp->x;
\r
5748 wpMain.y = lpwp->y;
\r
5754 /* [AS] Snapping */
\r
5755 case WM_ENTERSIZEMOVE:
\r
5756 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5757 if (hwnd == hwndMain) {
\r
5758 doingSizing = TRUE;
\r
5761 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5765 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5766 if (hwnd == hwndMain) {
\r
5767 lastSizing = wParam;
\r
5772 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5773 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5775 case WM_EXITSIZEMOVE:
\r
5776 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5777 if (hwnd == hwndMain) {
\r
5779 doingSizing = FALSE;
\r
5780 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5781 GetClientRect(hwnd, &client);
\r
5782 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5784 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5786 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5789 case WM_DESTROY: /* message: window being destroyed */
\r
5790 PostQuitMessage(0);
\r
5794 if (hwnd == hwndMain) {
\r
5799 default: /* Passes it on if unprocessed */
\r
5800 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5807 /*---------------------------------------------------------------------------*\
\r
5809 * Misc utility routines
\r
5811 \*---------------------------------------------------------------------------*/
\r
5814 * Decent random number generator, at least not as bad as Windows
\r
5815 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5817 unsigned int randstate;
\r
5822 randstate = randstate * 1664525 + 1013904223;
\r
5823 return (int) randstate & 0x7fffffff;
\r
5827 mysrandom(unsigned int seed)
\r
5834 * returns TRUE if user selects a different color, FALSE otherwise
\r
5838 ChangeColor(HWND hwnd, COLORREF *which)
\r
5840 static BOOL firstTime = TRUE;
\r
5841 static DWORD customColors[16];
\r
5843 COLORREF newcolor;
\r
5848 /* Make initial colors in use available as custom colors */
\r
5849 /* Should we put the compiled-in defaults here instead? */
\r
5851 customColors[i++] = lightSquareColor & 0xffffff;
\r
5852 customColors[i++] = darkSquareColor & 0xffffff;
\r
5853 customColors[i++] = whitePieceColor & 0xffffff;
\r
5854 customColors[i++] = blackPieceColor & 0xffffff;
\r
5855 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5856 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5858 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5859 customColors[i++] = textAttribs[ccl].color;
\r
5861 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5862 firstTime = FALSE;
\r
5865 cc.lStructSize = sizeof(cc);
\r
5866 cc.hwndOwner = hwnd;
\r
5867 cc.hInstance = NULL;
\r
5868 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5869 cc.lpCustColors = (LPDWORD) customColors;
\r
5870 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5872 if (!ChooseColor(&cc)) return FALSE;
\r
5874 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5875 if (newcolor == *which) return FALSE;
\r
5876 *which = newcolor;
\r
5880 InitDrawingColors();
\r
5881 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5886 MyLoadSound(MySound *ms)
\r
5892 if (ms->data && ms->flag) free(ms->data);
\r
5895 switch (ms->name[0]) {
\r
5901 /* System sound from Control Panel. Don't preload here. */
\r
5905 if (ms->name[1] == NULLCHAR) {
\r
5906 /* "!" alone = silence */
\r
5909 /* Builtin wave resource. Error if not found. */
\r
5910 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5911 if (h == NULL) break;
\r
5912 ms->data = (void *)LoadResource(hInst, h);
\r
5913 ms->flag = 0; // not maloced, so cannot be freed!
\r
5914 if (h == NULL) break;
\r
5919 /* .wav file. Error if not found. */
\r
5920 f = fopen(ms->name, "rb");
\r
5921 if (f == NULL) break;
\r
5922 if (fstat(fileno(f), &st) < 0) break;
\r
5923 ms->data = malloc(st.st_size);
\r
5925 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5931 char buf[MSG_SIZ];
\r
5932 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5933 DisplayError(buf, GetLastError());
\r
5939 MyPlaySound(MySound *ms)
\r
5941 BOOLEAN ok = FALSE;
\r
5943 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5944 switch (ms->name[0]) {
\r
5946 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5951 /* System sound from Control Panel (deprecated feature).
\r
5952 "$" alone or an unset sound name gets default beep (still in use). */
\r
5953 if (ms->name[1]) {
\r
5954 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5956 if (!ok) ok = MessageBeep(MB_OK);
\r
5959 /* Builtin wave resource, or "!" alone for silence */
\r
5960 if (ms->name[1]) {
\r
5961 if (ms->data == NULL) return FALSE;
\r
5962 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5968 /* .wav file. Error if not found. */
\r
5969 if (ms->data == NULL) return FALSE;
\r
5970 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5973 /* Don't print an error: this can happen innocently if the sound driver
\r
5974 is busy; for instance, if another instance of WinBoard is playing
\r
5975 a sound at about the same time. */
\r
5981 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5984 OPENFILENAME *ofn;
\r
5985 static UINT *number; /* gross that this is static */
\r
5987 switch (message) {
\r
5988 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5989 /* Center the dialog over the application window */
\r
5990 ofn = (OPENFILENAME *) lParam;
\r
5991 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5992 number = (UINT *) ofn->lCustData;
\r
5993 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5997 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5998 Translate(hDlg, 1536);
\r
5999 return FALSE; /* Allow for further processing */
\r
6002 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
6003 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
6005 return FALSE; /* Allow for further processing */
\r
6011 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
6013 static UINT *number;
\r
6014 OPENFILENAME *ofname;
\r
6017 case WM_INITDIALOG:
\r
6018 Translate(hdlg, DLG_IndexNumber);
\r
6019 ofname = (OPENFILENAME *)lParam;
\r
6020 number = (UINT *)(ofname->lCustData);
\r
6023 ofnot = (OFNOTIFY *)lParam;
\r
6024 if (ofnot->hdr.code == CDN_FILEOK) {
\r
6025 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
6034 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
6035 char *nameFilt, char *dlgTitle, UINT *number,
\r
6036 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
6038 OPENFILENAME openFileName;
\r
6039 char buf1[MSG_SIZ];
\r
6042 if (fileName == NULL) fileName = buf1;
\r
6043 if (defName == NULL) {
\r
6044 safeStrCpy(fileName, "*.", 3 );
\r
6045 strcat(fileName, defExt);
\r
6047 safeStrCpy(fileName, defName, MSG_SIZ );
\r
6049 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
6050 if (number) *number = 0;
\r
6052 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
6053 openFileName.hwndOwner = hwnd;
\r
6054 openFileName.hInstance = (HANDLE) hInst;
\r
6055 openFileName.lpstrFilter = nameFilt;
\r
6056 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
6057 openFileName.nMaxCustFilter = 0L;
\r
6058 openFileName.nFilterIndex = 1L;
\r
6059 openFileName.lpstrFile = fileName;
\r
6060 openFileName.nMaxFile = MSG_SIZ;
\r
6061 openFileName.lpstrFileTitle = fileTitle;
\r
6062 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
6063 openFileName.lpstrInitialDir = NULL;
\r
6064 openFileName.lpstrTitle = dlgTitle;
\r
6065 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
6066 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
6067 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
6068 | (oldDialog ? 0 : OFN_EXPLORER);
\r
6069 openFileName.nFileOffset = 0;
\r
6070 openFileName.nFileExtension = 0;
\r
6071 openFileName.lpstrDefExt = defExt;
\r
6072 openFileName.lCustData = (LONG) number;
\r
6073 openFileName.lpfnHook = oldDialog ?
\r
6074 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
6075 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
6077 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
6078 GetOpenFileName(&openFileName)) {
\r
6079 /* open the file */
\r
6080 f = fopen(openFileName.lpstrFile, write);
\r
6082 MessageBox(hwnd, _("File open failed"), NULL,
\r
6083 MB_OK|MB_ICONEXCLAMATION);
\r
6087 int err = CommDlgExtendedError();
\r
6088 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
6097 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
6099 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
6102 * Get the first pop-up menu in the menu template. This is the
\r
6103 * menu that TrackPopupMenu displays.
\r
6105 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
6106 TranslateOneMenu(10, hmenuTrackPopup);
\r
6108 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
6111 * TrackPopup uses screen coordinates, so convert the
\r
6112 * coordinates of the mouse click to screen coordinates.
\r
6114 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
6116 /* Draw and track the floating pop-up menu. */
\r
6117 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
6118 pt.x, pt.y, 0, hwnd, NULL);
\r
6120 /* Destroy the menu.*/
\r
6121 DestroyMenu(hmenu);
\r
6126 int sizeX, sizeY, newSizeX, newSizeY;
\r
6128 } ResizeEditPlusButtonsClosure;
\r
6131 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
6133 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
6137 if (hChild == cl->hText) return TRUE;
\r
6138 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
6139 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
6140 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
6141 ScreenToClient(cl->hDlg, &pt);
\r
6142 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
6143 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
6147 /* Resize a dialog that has a (rich) edit field filling most of
\r
6148 the top, with a row of buttons below */
\r
6150 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
6153 int newTextHeight, newTextWidth;
\r
6154 ResizeEditPlusButtonsClosure cl;
\r
6156 /*if (IsIconic(hDlg)) return;*/
\r
6157 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
6159 cl.hdwp = BeginDeferWindowPos(8);
\r
6161 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
6162 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6163 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6164 if (newTextHeight < 0) {
\r
6165 newSizeY += -newTextHeight;
\r
6166 newTextHeight = 0;
\r
6168 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
6169 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6175 cl.newSizeX = newSizeX;
\r
6176 cl.newSizeY = newSizeY;
\r
6177 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
6179 EndDeferWindowPos(cl.hdwp);
\r
6182 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
6184 RECT rChild, rParent;
\r
6185 int wChild, hChild, wParent, hParent;
\r
6186 int wScreen, hScreen, xNew, yNew;
\r
6189 /* Get the Height and Width of the child window */
\r
6190 GetWindowRect (hwndChild, &rChild);
\r
6191 wChild = rChild.right - rChild.left;
\r
6192 hChild = rChild.bottom - rChild.top;
\r
6194 /* Get the Height and Width of the parent window */
\r
6195 GetWindowRect (hwndParent, &rParent);
\r
6196 wParent = rParent.right - rParent.left;
\r
6197 hParent = rParent.bottom - rParent.top;
\r
6199 /* Get the display limits */
\r
6200 hdc = GetDC (hwndChild);
\r
6201 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
6202 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
6203 ReleaseDC(hwndChild, hdc);
\r
6205 /* Calculate new X position, then adjust for screen */
\r
6206 xNew = rParent.left + ((wParent - wChild) /2);
\r
6209 } else if ((xNew+wChild) > wScreen) {
\r
6210 xNew = wScreen - wChild;
\r
6213 /* Calculate new Y position, then adjust for screen */
\r
6215 yNew = rParent.top + ((hParent - hChild) /2);
\r
6218 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
6223 } else if ((yNew+hChild) > hScreen) {
\r
6224 yNew = hScreen - hChild;
\r
6227 /* Set it, and return */
\r
6228 return SetWindowPos (hwndChild, NULL,
\r
6229 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
6232 /* Center one window over another */
\r
6233 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
6235 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
6238 /*---------------------------------------------------------------------------*\
\r
6240 * Startup Dialog functions
\r
6242 \*---------------------------------------------------------------------------*/
\r
6244 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
6246 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6248 while (*cd != NULL) {
\r
6249 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
6255 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
6257 char buf1[MAX_ARG_LEN];
\r
6260 if (str[0] == '@') {
\r
6261 FILE* f = fopen(str + 1, "r");
\r
6263 DisplayFatalError(str + 1, errno, 2);
\r
6266 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
6268 buf1[len] = NULLCHAR;
\r
6272 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6275 char buf[MSG_SIZ];
\r
6276 char *end = strchr(str, '\n');
\r
6277 if (end == NULL) return;
\r
6278 memcpy(buf, str, end - str);
\r
6279 buf[end - str] = NULLCHAR;
\r
6280 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6286 SetStartupDialogEnables(HWND hDlg)
\r
6288 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6289 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6290 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6291 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6292 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6293 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6294 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6295 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6296 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6297 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6298 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6299 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6300 IsDlgButtonChecked(hDlg, OPT_View));
\r
6304 QuoteForFilename(char *filename)
\r
6306 int dquote, space;
\r
6307 dquote = strchr(filename, '"') != NULL;
\r
6308 space = strchr(filename, ' ') != NULL;
\r
6309 if (dquote || space) {
\r
6321 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6323 char buf[MSG_SIZ];
\r
6326 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6327 q = QuoteForFilename(nthcp);
\r
6328 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6329 if (*nthdir != NULLCHAR) {
\r
6330 q = QuoteForFilename(nthdir);
\r
6331 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6333 if (*nthcp == NULLCHAR) {
\r
6334 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6335 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6336 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6337 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6342 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6344 char buf[MSG_SIZ];
\r
6348 switch (message) {
\r
6349 case WM_INITDIALOG:
\r
6350 /* Center the dialog */
\r
6351 CenterWindow (hDlg, GetDesktopWindow());
\r
6352 Translate(hDlg, DLG_Startup);
\r
6353 /* Initialize the dialog items */
\r
6354 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6355 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6356 firstChessProgramNames);
\r
6357 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6358 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6359 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6360 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6361 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6362 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6363 if (*appData.icsHelper != NULLCHAR) {
\r
6364 char *q = QuoteForFilename(appData.icsHelper);
\r
6365 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6367 if (*appData.icsHost == NULLCHAR) {
\r
6368 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6369 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6370 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6371 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6372 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6375 if (appData.icsActive) {
\r
6376 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6378 else if (appData.noChessProgram) {
\r
6379 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6382 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6385 SetStartupDialogEnables(hDlg);
\r
6389 switch (LOWORD(wParam)) {
\r
6391 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6392 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6393 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6395 comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox
\r
6396 ParseArgs(StringGet, &p);
\r
6397 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6398 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6400 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6401 ParseArgs(StringGet, &p);
\r
6402 SwapEngines(singleList); // ... and then make it 'second'
\r
6404 appData.noChessProgram = FALSE;
\r
6405 appData.icsActive = FALSE;
\r
6406 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6407 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6408 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6410 ParseArgs(StringGet, &p);
\r
6411 if (appData.zippyPlay) {
\r
6412 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6413 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6415 ParseArgs(StringGet, &p);
\r
6417 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6418 appData.noChessProgram = TRUE;
\r
6419 appData.icsActive = FALSE;
\r
6421 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6422 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6425 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6426 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6428 ParseArgs(StringGet, &p);
\r
6430 EndDialog(hDlg, TRUE);
\r
6437 case IDM_HELPCONTENTS:
\r
6438 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6439 MessageBox (GetFocus(),
\r
6440 _("Unable to activate help"),
\r
6441 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6446 SetStartupDialogEnables(hDlg);
\r
6454 /*---------------------------------------------------------------------------*\
\r
6456 * About box dialog functions
\r
6458 \*---------------------------------------------------------------------------*/
\r
6460 /* Process messages for "About" dialog box */
\r
6462 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6464 switch (message) {
\r
6465 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6466 /* Center the dialog over the application window */
\r
6467 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6468 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6469 Translate(hDlg, ABOUTBOX);
\r
6473 case WM_COMMAND: /* message: received a command */
\r
6474 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6475 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6476 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6484 /*---------------------------------------------------------------------------*\
\r
6486 * Comment Dialog functions
\r
6488 \*---------------------------------------------------------------------------*/
\r
6491 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6493 static HANDLE hwndText = NULL;
\r
6494 int len, newSizeX, newSizeY;
\r
6495 static int sizeX, sizeY;
\r
6500 switch (message) {
\r
6501 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6502 /* Initialize the dialog items */
\r
6503 Translate(hDlg, DLG_EditComment);
\r
6504 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6505 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6506 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6507 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6508 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6509 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6510 SetWindowText(hDlg, commentTitle);
\r
6511 if (editComment) {
\r
6512 SetFocus(hwndText);
\r
6514 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6516 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6517 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6518 MAKELPARAM(FALSE, 0));
\r
6519 /* Size and position the dialog */
\r
6520 if (!commentDialog) {
\r
6521 commentDialog = hDlg;
\r
6522 GetClientRect(hDlg, &rect);
\r
6523 sizeX = rect.right;
\r
6524 sizeY = rect.bottom;
\r
6525 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6526 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6527 WINDOWPLACEMENT wp;
\r
6528 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6529 wp.length = sizeof(WINDOWPLACEMENT);
\r
6531 wp.showCmd = SW_SHOW;
\r
6532 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6533 wp.rcNormalPosition.left = wpComment.x;
\r
6534 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6535 wp.rcNormalPosition.top = wpComment.y;
\r
6536 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6537 SetWindowPlacement(hDlg, &wp);
\r
6539 GetClientRect(hDlg, &rect);
\r
6540 newSizeX = rect.right;
\r
6541 newSizeY = rect.bottom;
\r
6542 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6543 newSizeX, newSizeY);
\r
6548 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6551 case WM_COMMAND: /* message: received a command */
\r
6552 switch (LOWORD(wParam)) {
\r
6554 if (editComment) {
\r
6556 /* Read changed options from the dialog box */
\r
6557 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6558 len = GetWindowTextLength(hwndText);
\r
6559 str = (char *) malloc(len + 1);
\r
6560 GetWindowText(hwndText, str, len + 1);
\r
6569 ReplaceComment(commentIndex, str);
\r
6576 case OPT_CancelComment:
\r
6580 case OPT_ClearComment:
\r
6581 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6584 case OPT_EditComment:
\r
6585 EditCommentEvent();
\r
6593 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6594 if( wParam == OPT_CommentText ) {
\r
6595 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6597 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6598 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6602 pt.x = LOWORD( lpMF->lParam );
\r
6603 pt.y = HIWORD( lpMF->lParam );
\r
6605 if(lpMF->msg == WM_CHAR) {
\r
6607 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6608 index = sel.cpMin;
\r
6610 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6612 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6613 len = GetWindowTextLength(hwndText);
\r
6614 str = (char *) malloc(len + 1);
\r
6615 GetWindowText(hwndText, str, len + 1);
\r
6616 ReplaceComment(commentIndex, str);
\r
6617 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6618 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6621 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6622 lpMF->msg = WM_USER;
\r
6630 newSizeX = LOWORD(lParam);
\r
6631 newSizeY = HIWORD(lParam);
\r
6632 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6637 case WM_GETMINMAXINFO:
\r
6638 /* Prevent resizing window too small */
\r
6639 mmi = (MINMAXINFO *) lParam;
\r
6640 mmi->ptMinTrackSize.x = 100;
\r
6641 mmi->ptMinTrackSize.y = 100;
\r
6648 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6653 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6655 if (str == NULL) str = "";
\r
6656 p = (char *) malloc(2 * strlen(str) + 2);
\r
6659 if (*str == '\n') *q++ = '\r';
\r
6663 if (commentText != NULL) free(commentText);
\r
6665 commentIndex = index;
\r
6666 commentTitle = title;
\r
6668 editComment = edit;
\r
6670 if (commentDialog) {
\r
6671 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6672 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6674 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6675 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6676 hwndMain, (DLGPROC)lpProc);
\r
6677 FreeProcInstance(lpProc);
\r
6683 /*---------------------------------------------------------------------------*\
\r
6685 * Type-in move dialog functions
\r
6687 \*---------------------------------------------------------------------------*/
\r
6690 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6692 char move[MSG_SIZ];
\r
6695 switch (message) {
\r
6696 case WM_INITDIALOG:
\r
6697 move[0] = (char) lParam;
\r
6698 move[1] = NULLCHAR;
\r
6699 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6700 Translate(hDlg, DLG_TypeInMove);
\r
6701 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6702 SetWindowText(hInput, move);
\r
6704 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6708 switch (LOWORD(wParam)) {
\r
6711 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6712 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6713 TypeInDoneEvent(move);
\r
6714 EndDialog(hDlg, TRUE);
\r
6717 EndDialog(hDlg, FALSE);
\r
6728 PopUpMoveDialog(char firstchar)
\r
6732 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6733 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6734 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6735 FreeProcInstance(lpProc);
\r
6738 /*---------------------------------------------------------------------------*\
\r
6740 * Type-in name dialog functions
\r
6742 \*---------------------------------------------------------------------------*/
\r
6745 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6747 char move[MSG_SIZ];
\r
6750 switch (message) {
\r
6751 case WM_INITDIALOG:
\r
6752 move[0] = (char) lParam;
\r
6753 move[1] = NULLCHAR;
\r
6754 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6755 Translate(hDlg, DLG_TypeInName);
\r
6756 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6757 SetWindowText(hInput, move);
\r
6759 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6763 switch (LOWORD(wParam)) {
\r
6765 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6766 appData.userName = strdup(move);
\r
6767 SetUserLogo(); DisplayLogos();
\r
6769 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6770 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6771 DisplayTitle(move);
\r
6775 EndDialog(hDlg, TRUE);
\r
6778 EndDialog(hDlg, FALSE);
\r
6789 PopUpNameDialog(char firstchar)
\r
6793 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6794 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6795 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6796 FreeProcInstance(lpProc);
\r
6799 /*---------------------------------------------------------------------------*\
\r
6803 \*---------------------------------------------------------------------------*/
\r
6805 /* Nonmodal error box */
\r
6806 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6807 WPARAM wParam, LPARAM lParam);
\r
6810 ErrorPopUp(char *title, char *content)
\r
6814 BOOLEAN modal = hwndMain == NULL;
\r
6832 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6833 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6836 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6838 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6839 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6840 hwndMain, (DLGPROC)lpProc);
\r
6841 FreeProcInstance(lpProc);
\r
6848 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6849 if (errorDialog == NULL) return;
\r
6850 DestroyWindow(errorDialog);
\r
6851 errorDialog = NULL;
\r
6852 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6856 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6860 switch (message) {
\r
6861 case WM_INITDIALOG:
\r
6862 GetWindowRect(hDlg, &rChild);
\r
6865 SetWindowPos(hDlg, NULL, rChild.left,
\r
6866 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6867 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6871 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6872 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6873 and it doesn't work when you resize the dialog.
\r
6874 For now, just give it a default position.
\r
6876 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6877 Translate(hDlg, DLG_Error);
\r
6879 errorDialog = hDlg;
\r
6880 SetWindowText(hDlg, errorTitle);
\r
6881 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6885 switch (LOWORD(wParam)) {
\r
6888 if (errorDialog == hDlg) errorDialog = NULL;
\r
6889 DestroyWindow(hDlg);
\r
6901 HWND gothicDialog = NULL;
\r
6904 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6907 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6909 switch (message) {
\r
6910 case WM_INITDIALOG:
\r
6911 GetWindowRect(hDlg, &rChild);
\r
6913 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6917 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6918 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6919 and it doesn't work when you resize the dialog.
\r
6920 For now, just give it a default position.
\r
6922 gothicDialog = hDlg;
\r
6923 SetWindowText(hDlg, errorTitle);
\r
6924 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6928 switch (LOWORD(wParam)) {
\r
6931 if (errorDialog == hDlg) errorDialog = NULL;
\r
6932 DestroyWindow(hDlg);
\r
6944 GothicPopUp(char *title, VariantClass variant)
\r
6947 static char *lastTitle;
\r
6949 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6950 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6952 if(lastTitle != title && gothicDialog != NULL) {
\r
6953 DestroyWindow(gothicDialog);
\r
6954 gothicDialog = NULL;
\r
6956 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6957 title = lastTitle;
\r
6958 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6959 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6960 hwndMain, (DLGPROC)lpProc);
\r
6961 FreeProcInstance(lpProc);
\r
6966 /*---------------------------------------------------------------------------*\
\r
6968 * Ics Interaction console functions
\r
6970 \*---------------------------------------------------------------------------*/
\r
6972 #define HISTORY_SIZE 64
\r
6973 static char *history[HISTORY_SIZE];
\r
6974 int histIn = 0, histP = 0;
\r
6978 SaveInHistory(char *cmd)
\r
6980 if (history[histIn] != NULL) {
\r
6981 free(history[histIn]);
\r
6982 history[histIn] = NULL;
\r
6984 if (*cmd == NULLCHAR) return;
\r
6985 history[histIn] = StrSave(cmd);
\r
6986 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6987 if (history[histIn] != NULL) {
\r
6988 free(history[histIn]);
\r
6990 history[histIn] = NULL;
\r
6996 PrevInHistory(char *cmd)
\r
6999 if (histP == histIn) {
\r
7000 if (history[histIn] != NULL) free(history[histIn]);
\r
7001 history[histIn] = StrSave(cmd);
\r
7003 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
7004 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
7006 return history[histP];
\r
7012 if (histP == histIn) return NULL;
\r
7013 histP = (histP + 1) % HISTORY_SIZE;
\r
7014 return history[histP];
\r
7018 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
7022 hmenu = LoadMenu(hInst, "TextMenu");
\r
7023 h = GetSubMenu(hmenu, 0);
\r
7025 if (strcmp(e->item, "-") == 0) {
\r
7026 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
7027 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
7028 int flags = MF_STRING, j = 0;
\r
7029 if (e->item[0] == '|') {
\r
7030 flags |= MF_MENUBARBREAK;
\r
7033 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
7034 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
7042 WNDPROC consoleTextWindowProc;
\r
7045 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
7047 char buf[MSG_SIZ], name[MSG_SIZ];
\r
7048 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7052 SetWindowText(hInput, command);
\r
7054 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
7056 sel.cpMin = 999999;
\r
7057 sel.cpMax = 999999;
\r
7058 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7063 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7064 if (sel.cpMin == sel.cpMax) {
\r
7065 /* Expand to surrounding word */
\r
7068 tr.chrg.cpMax = sel.cpMin;
\r
7069 tr.chrg.cpMin = --sel.cpMin;
\r
7070 if (sel.cpMin < 0) break;
\r
7071 tr.lpstrText = name;
\r
7072 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
7073 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
7077 tr.chrg.cpMin = sel.cpMax;
\r
7078 tr.chrg.cpMax = ++sel.cpMax;
\r
7079 tr.lpstrText = name;
\r
7080 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
7081 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
7084 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
7085 MessageBeep(MB_ICONEXCLAMATION);
\r
7089 tr.lpstrText = name;
\r
7090 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
7092 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
7093 MessageBeep(MB_ICONEXCLAMATION);
\r
7096 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
7099 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
7100 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
7101 SetWindowText(hInput, buf);
\r
7102 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
7104 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
7105 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
7106 SetWindowText(hInput, buf);
\r
7107 sel.cpMin = 999999;
\r
7108 sel.cpMax = 999999;
\r
7109 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7115 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7120 switch (message) {
\r
7122 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7123 if(wParam=='R') return 0;
\r
7126 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
7129 sel.cpMin = 999999;
\r
7130 sel.cpMax = 999999;
\r
7131 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7132 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
7137 if(wParam != '\022') {
\r
7138 if (wParam == '\t') {
\r
7139 if (GetKeyState(VK_SHIFT) < 0) {
\r
7141 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7142 if (buttonDesc[0].hwnd) {
\r
7143 SetFocus(buttonDesc[0].hwnd);
\r
7145 SetFocus(hwndMain);
\r
7149 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
7152 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7153 JAWS_DELETE( SetFocus(hInput); )
\r
7154 SendMessage(hInput, message, wParam, lParam);
\r
7157 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
7159 case WM_RBUTTONDOWN:
\r
7160 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
7161 /* Move selection here if it was empty */
\r
7163 pt.x = LOWORD(lParam);
\r
7164 pt.y = HIWORD(lParam);
\r
7165 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7166 if (sel.cpMin == sel.cpMax) {
\r
7167 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
7168 sel.cpMax = sel.cpMin;
\r
7169 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7171 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
7172 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
7174 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
7175 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7176 if (sel.cpMin == sel.cpMax) {
\r
7177 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7178 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
7180 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7181 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7183 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
7184 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
7185 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
7186 MenuPopup(hwnd, pt, hmenu, -1);
\r
7190 case WM_RBUTTONUP:
\r
7191 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7192 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7193 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7197 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7199 return SendMessage(hInput, message, wParam, lParam);
\r
7200 case WM_MBUTTONDOWN:
\r
7201 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7203 switch (LOWORD(wParam)) {
\r
7204 case IDM_QuickPaste:
\r
7206 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7207 if (sel.cpMin == sel.cpMax) {
\r
7208 MessageBeep(MB_ICONEXCLAMATION);
\r
7211 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7212 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7213 SendMessage(hInput, WM_PASTE, 0, 0);
\r
7218 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7221 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7224 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7228 int i = LOWORD(wParam) - IDM_CommandX;
\r
7229 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
7230 icsTextMenuEntry[i].command != NULL) {
\r
7231 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
7232 icsTextMenuEntry[i].getname,
\r
7233 icsTextMenuEntry[i].immediate);
\r
7241 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
7244 WNDPROC consoleInputWindowProc;
\r
7247 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7249 char buf[MSG_SIZ];
\r
7251 static BOOL sendNextChar = FALSE;
\r
7252 static BOOL quoteNextChar = FALSE;
\r
7253 InputSource *is = consoleInputSource;
\r
7257 switch (message) {
\r
7259 if (!appData.localLineEditing || sendNextChar) {
\r
7260 is->buf[0] = (CHAR) wParam;
\r
7262 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7263 sendNextChar = FALSE;
\r
7266 if (quoteNextChar) {
\r
7267 buf[0] = (char) wParam;
\r
7268 buf[1] = NULLCHAR;
\r
7269 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7270 quoteNextChar = FALSE;
\r
7274 case '\r': /* Enter key */
\r
7275 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7276 if (consoleEcho) SaveInHistory(is->buf);
\r
7277 is->buf[is->count++] = '\n';
\r
7278 is->buf[is->count] = NULLCHAR;
\r
7279 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7280 if (consoleEcho) {
\r
7281 ConsoleOutput(is->buf, is->count, TRUE);
\r
7282 } else if (appData.localLineEditing) {
\r
7283 ConsoleOutput("\n", 1, TRUE);
\r
7286 case '\033': /* Escape key */
\r
7287 SetWindowText(hwnd, "");
\r
7288 cf.cbSize = sizeof(CHARFORMAT);
\r
7289 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7290 if (consoleEcho) {
\r
7291 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7293 cf.crTextColor = COLOR_ECHOOFF;
\r
7295 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7296 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7298 case '\t': /* Tab key */
\r
7299 if (GetKeyState(VK_SHIFT) < 0) {
\r
7301 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7304 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7305 if (buttonDesc[0].hwnd) {
\r
7306 SetFocus(buttonDesc[0].hwnd);
\r
7308 SetFocus(hwndMain);
\r
7312 case '\023': /* Ctrl+S */
\r
7313 sendNextChar = TRUE;
\r
7315 case '\021': /* Ctrl+Q */
\r
7316 quoteNextChar = TRUE;
\r
7326 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7327 p = PrevInHistory(buf);
\r
7329 SetWindowText(hwnd, p);
\r
7330 sel.cpMin = 999999;
\r
7331 sel.cpMax = 999999;
\r
7332 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7337 p = NextInHistory();
\r
7339 SetWindowText(hwnd, p);
\r
7340 sel.cpMin = 999999;
\r
7341 sel.cpMax = 999999;
\r
7342 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7348 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7352 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7356 case WM_MBUTTONDOWN:
\r
7357 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7358 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7360 case WM_RBUTTONUP:
\r
7361 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7362 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7363 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7367 hmenu = LoadMenu(hInst, "InputMenu");
\r
7368 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7369 if (sel.cpMin == sel.cpMax) {
\r
7370 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7371 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7373 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7374 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7376 pt.x = LOWORD(lParam);
\r
7377 pt.y = HIWORD(lParam);
\r
7378 MenuPopup(hwnd, pt, hmenu, -1);
\r
7382 switch (LOWORD(wParam)) {
\r
7384 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7386 case IDM_SelectAll:
\r
7388 sel.cpMax = -1; /*999999?*/
\r
7389 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7392 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7395 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7398 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7403 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7406 #define CO_MAX 100000
\r
7407 #define CO_TRIM 1000
\r
7410 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7412 static SnapData sd;
\r
7413 HWND hText, hInput;
\r
7415 static int sizeX, sizeY;
\r
7416 int newSizeX, newSizeY;
\r
7420 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7421 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7423 switch (message) {
\r
7425 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7427 ENLINK *pLink = (ENLINK*)lParam;
\r
7428 if (pLink->msg == WM_LBUTTONUP)
\r
7432 tr.chrg = pLink->chrg;
\r
7433 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7434 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7435 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7436 free(tr.lpstrText);
\r
7440 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7441 hwndConsole = hDlg;
\r
7443 consoleTextWindowProc = (WNDPROC)
\r
7444 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7445 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7446 consoleInputWindowProc = (WNDPROC)
\r
7447 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7448 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7449 Colorize(ColorNormal, TRUE);
\r
7450 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7451 ChangedConsoleFont();
\r
7452 GetClientRect(hDlg, &rect);
\r
7453 sizeX = rect.right;
\r
7454 sizeY = rect.bottom;
\r
7455 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7456 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7457 WINDOWPLACEMENT wp;
\r
7458 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7459 wp.length = sizeof(WINDOWPLACEMENT);
\r
7461 wp.showCmd = SW_SHOW;
\r
7462 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7463 wp.rcNormalPosition.left = wpConsole.x;
\r
7464 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7465 wp.rcNormalPosition.top = wpConsole.y;
\r
7466 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7467 SetWindowPlacement(hDlg, &wp);
\r
7470 // [HGM] Chessknight's change 2004-07-13
\r
7471 else { /* Determine Defaults */
\r
7472 WINDOWPLACEMENT wp;
\r
7473 wpConsole.x = wpMain.width + 1;
\r
7474 wpConsole.y = wpMain.y;
\r
7475 wpConsole.width = screenWidth - wpMain.width;
\r
7476 wpConsole.height = wpMain.height;
\r
7477 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7478 wp.length = sizeof(WINDOWPLACEMENT);
\r
7480 wp.showCmd = SW_SHOW;
\r
7481 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7482 wp.rcNormalPosition.left = wpConsole.x;
\r
7483 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7484 wp.rcNormalPosition.top = wpConsole.y;
\r
7485 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7486 SetWindowPlacement(hDlg, &wp);
\r
7489 // Allow hText to highlight URLs and send notifications on them
\r
7490 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7491 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7492 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7493 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7507 if (IsIconic(hDlg)) break;
\r
7508 newSizeX = LOWORD(lParam);
\r
7509 newSizeY = HIWORD(lParam);
\r
7510 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7511 RECT rectText, rectInput;
\r
7513 int newTextHeight, newTextWidth;
\r
7514 GetWindowRect(hText, &rectText);
\r
7515 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7516 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7517 if (newTextHeight < 0) {
\r
7518 newSizeY += -newTextHeight;
\r
7519 newTextHeight = 0;
\r
7521 SetWindowPos(hText, NULL, 0, 0,
\r
7522 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7523 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7524 pt.x = rectInput.left;
\r
7525 pt.y = rectInput.top + newSizeY - sizeY;
\r
7526 ScreenToClient(hDlg, &pt);
\r
7527 SetWindowPos(hInput, NULL,
\r
7528 pt.x, pt.y, /* needs client coords */
\r
7529 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7530 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7536 case WM_GETMINMAXINFO:
\r
7537 /* Prevent resizing window too small */
\r
7538 mmi = (MINMAXINFO *) lParam;
\r
7539 mmi->ptMinTrackSize.x = 100;
\r
7540 mmi->ptMinTrackSize.y = 100;
\r
7543 /* [AS] Snapping */
\r
7544 case WM_ENTERSIZEMOVE:
\r
7545 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7548 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7551 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7553 case WM_EXITSIZEMOVE:
\r
7554 UpdateICSWidth(hText);
\r
7555 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7558 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7566 if (hwndConsole) return;
\r
7567 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7568 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7573 ConsoleOutput(char* data, int length, int forceVisible)
\r
7578 char buf[CO_MAX+1];
\r
7581 static int delayLF = 0;
\r
7582 CHARRANGE savesel, sel;
\r
7584 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7592 while (length--) {
\r
7600 } else if (*p == '\007') {
\r
7601 MyPlaySound(&sounds[(int)SoundBell]);
\r
7608 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7609 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7610 /* Save current selection */
\r
7611 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7612 exlen = GetWindowTextLength(hText);
\r
7613 /* Find out whether current end of text is visible */
\r
7614 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7615 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7616 /* Trim existing text if it's too long */
\r
7617 if (exlen + (q - buf) > CO_MAX) {
\r
7618 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7621 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7622 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7624 savesel.cpMin -= trim;
\r
7625 savesel.cpMax -= trim;
\r
7626 if (exlen < 0) exlen = 0;
\r
7627 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7628 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7630 /* Append the new text */
\r
7631 sel.cpMin = exlen;
\r
7632 sel.cpMax = exlen;
\r
7633 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7634 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7635 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7636 if (forceVisible || exlen == 0 ||
\r
7637 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7638 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7639 /* Scroll to make new end of text visible if old end of text
\r
7640 was visible or new text is an echo of user typein */
\r
7641 sel.cpMin = 9999999;
\r
7642 sel.cpMax = 9999999;
\r
7643 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7644 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7645 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7646 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7648 if (savesel.cpMax == exlen || forceVisible) {
\r
7649 /* Move insert point to new end of text if it was at the old
\r
7650 end of text or if the new text is an echo of user typein */
\r
7651 sel.cpMin = 9999999;
\r
7652 sel.cpMax = 9999999;
\r
7653 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7655 /* Restore previous selection */
\r
7656 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7658 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7665 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7669 COLORREF oldFg, oldBg;
\r
7673 if(copyNumber > 1)
\r
7674 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7676 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7677 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7678 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7681 rect.right = x + squareSize;
\r
7683 rect.bottom = y + squareSize;
\r
7686 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7687 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7688 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7689 &rect, str, strlen(str), NULL);
\r
7691 (void) SetTextColor(hdc, oldFg);
\r
7692 (void) SetBkColor(hdc, oldBg);
\r
7693 (void) SelectObject(hdc, oldFont);
\r
7697 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7698 RECT *rect, char *color, char *flagFell)
\r
7702 COLORREF oldFg, oldBg;
\r
7705 if (twoBoards && partnerUp) return;
\r
7706 if (appData.clockMode) {
\r
7707 if (tinyLayout == 2)
\r
7708 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7710 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7717 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7718 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7720 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7721 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7724 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7728 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7729 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7730 rect, str, strlen(str), NULL);
\r
7731 if(logoHeight > 0 && appData.clockMode) {
\r
7733 str += strlen(color)+2;
\r
7734 r.top = rect->top + logoHeight/2;
\r
7735 r.left = rect->left;
\r
7736 r.right = rect->right;
\r
7737 r.bottom = rect->bottom;
\r
7738 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7739 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7740 &r, str, strlen(str), NULL);
\r
7742 (void) SetTextColor(hdc, oldFg);
\r
7743 (void) SetBkColor(hdc, oldBg);
\r
7744 (void) SelectObject(hdc, oldFont);
\r
7749 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7755 if( count <= 0 ) {
\r
7756 if (appData.debugMode) {
\r
7757 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7760 return ERROR_INVALID_USER_BUFFER;
\r
7763 ResetEvent(ovl->hEvent);
\r
7764 ovl->Offset = ovl->OffsetHigh = 0;
\r
7765 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7769 err = GetLastError();
\r
7770 if (err == ERROR_IO_PENDING) {
\r
7771 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7775 err = GetLastError();
\r
7782 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7787 ResetEvent(ovl->hEvent);
\r
7788 ovl->Offset = ovl->OffsetHigh = 0;
\r
7789 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7793 err = GetLastError();
\r
7794 if (err == ERROR_IO_PENDING) {
\r
7795 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7799 err = GetLastError();
\r
7806 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7807 void CheckForInputBufferFull( InputSource * is )
\r
7809 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7810 /* Look for end of line */
\r
7811 char * p = is->buf;
\r
7813 while( p < is->next && *p != '\n' ) {
\r
7817 if( p >= is->next ) {
\r
7818 if (appData.debugMode) {
\r
7819 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7822 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7823 is->count = (DWORD) -1;
\r
7824 is->next = is->buf;
\r
7830 InputThread(LPVOID arg)
\r
7835 is = (InputSource *) arg;
\r
7836 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7837 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7838 while (is->hThread != NULL) {
\r
7839 is->error = DoReadFile(is->hFile, is->next,
\r
7840 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7841 &is->count, &ovl);
\r
7842 if (is->error == NO_ERROR) {
\r
7843 is->next += is->count;
\r
7845 if (is->error == ERROR_BROKEN_PIPE) {
\r
7846 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7849 is->count = (DWORD) -1;
\r
7850 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7855 CheckForInputBufferFull( is );
\r
7857 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7859 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7861 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7864 CloseHandle(ovl.hEvent);
\r
7865 CloseHandle(is->hFile);
\r
7867 if (appData.debugMode) {
\r
7868 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7875 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7877 NonOvlInputThread(LPVOID arg)
\r
7884 is = (InputSource *) arg;
\r
7885 while (is->hThread != NULL) {
\r
7886 is->error = ReadFile(is->hFile, is->next,
\r
7887 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7888 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7889 if (is->error == NO_ERROR) {
\r
7890 /* Change CRLF to LF */
\r
7891 if (is->next > is->buf) {
\r
7893 i = is->count + 1;
\r
7901 if (prev == '\r' && *p == '\n') {
\r
7913 if (is->error == ERROR_BROKEN_PIPE) {
\r
7914 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7917 is->count = (DWORD) -1;
\r
7921 CheckForInputBufferFull( is );
\r
7923 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7925 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7927 if (is->count < 0) break; /* Quit on error */
\r
7929 CloseHandle(is->hFile);
\r
7934 SocketInputThread(LPVOID arg)
\r
7938 is = (InputSource *) arg;
\r
7939 while (is->hThread != NULL) {
\r
7940 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7941 if ((int)is->count == SOCKET_ERROR) {
\r
7942 is->count = (DWORD) -1;
\r
7943 is->error = WSAGetLastError();
\r
7945 is->error = NO_ERROR;
\r
7946 is->next += is->count;
\r
7947 if (is->count == 0 && is->second == is) {
\r
7948 /* End of file on stderr; quit with no message */
\r
7952 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7954 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7956 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7962 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7966 is = (InputSource *) lParam;
\r
7967 if (is->lineByLine) {
\r
7968 /* Feed in lines one by one */
\r
7969 char *p = is->buf;
\r
7971 while (q < is->next) {
\r
7972 if (*q++ == '\n') {
\r
7973 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7978 /* Move any partial line to the start of the buffer */
\r
7980 while (p < is->next) {
\r
7985 if (is->error != NO_ERROR || is->count == 0) {
\r
7986 /* Notify backend of the error. Note: If there was a partial
\r
7987 line at the end, it is not flushed through. */
\r
7988 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7991 /* Feed in the whole chunk of input at once */
\r
7992 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7993 is->next = is->buf;
\r
7997 /*---------------------------------------------------------------------------*\
\r
7999 * Menu enables. Used when setting various modes.
\r
8001 \*---------------------------------------------------------------------------*/
\r
8009 GreyRevert(Boolean grey)
\r
8010 { // [HGM] vari: for retracting variations in local mode
\r
8011 HMENU hmenu = GetMenu(hwndMain);
\r
8012 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
8013 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
8017 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
8019 while (enab->item > 0) {
\r
8020 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
8025 Enables gnuEnables[] = {
\r
8026 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
8027 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
8028 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
8029 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
8030 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
8031 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
8032 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
8033 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
8034 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
8035 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
8036 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
8037 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
8038 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
8041 // Needed to switch from ncp to GNU mode on Engine Load
\r
8042 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
8043 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
8044 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
8045 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
8046 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
8047 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
8048 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
8049 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
8050 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
8051 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
8052 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
8053 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8054 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
8055 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
8059 Enables icsEnables[] = {
\r
8060 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
8061 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
8062 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8063 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8064 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8065 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8066 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
8067 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
8068 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
8069 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
8070 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8071 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
8072 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
8073 { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },
\r
8074 { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },
\r
8075 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
8076 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
8077 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
8078 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
8079 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
8084 Enables zippyEnables[] = {
\r
8085 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8086 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
8087 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
8088 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
8093 Enables ncpEnables[] = {
\r
8094 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
8095 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
8096 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8097 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8098 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8099 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8100 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
8101 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
8102 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
8103 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
8104 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
8105 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
8106 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8107 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
8108 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
8109 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
8110 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
8111 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
8112 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
8113 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
8114 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
8115 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
8119 Enables trainingOnEnables[] = {
\r
8120 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
8121 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
8122 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
8123 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
8124 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
8125 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
8126 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
8127 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8128 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
8132 Enables trainingOffEnables[] = {
\r
8133 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
8134 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
8135 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
8136 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
8137 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
8138 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
8139 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
8140 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8141 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
8145 /* These modify either ncpEnables or gnuEnables */
\r
8146 Enables cmailEnables[] = {
\r
8147 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
8148 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
8149 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
8150 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
8151 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
8152 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
8153 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
8157 Enables machineThinkingEnables[] = {
\r
8158 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8159 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
8160 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
8161 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8162 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
8163 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8164 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8165 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8166 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8167 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
8168 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8169 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8170 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8171 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8172 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
8173 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
8177 Enables userThinkingEnables[] = {
\r
8178 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8179 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
8180 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
8181 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8182 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
8183 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8184 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8185 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8186 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8187 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
8188 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
8189 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
8190 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
8191 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
8192 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
8193 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
8197 /*---------------------------------------------------------------------------*\
\r
8199 * Front-end interface functions exported by XBoard.
\r
8200 * Functions appear in same order as prototypes in frontend.h.
\r
8202 \*---------------------------------------------------------------------------*/
\r
8204 CheckMark(UINT item, int state)
\r
8206 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
8212 static UINT prevChecked = 0;
\r
8213 static int prevPausing = 0;
\r
8216 if (pausing != prevPausing) {
\r
8217 prevPausing = pausing;
\r
8218 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
8219 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
8220 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
8223 switch (gameMode) {
\r
8224 case BeginningOfGame:
\r
8225 if (appData.icsActive)
\r
8226 nowChecked = IDM_IcsClient;
\r
8227 else if (appData.noChessProgram)
\r
8228 nowChecked = IDM_EditGame;
\r
8230 nowChecked = IDM_MachineBlack;
\r
8232 case MachinePlaysBlack:
\r
8233 nowChecked = IDM_MachineBlack;
\r
8235 case MachinePlaysWhite:
\r
8236 nowChecked = IDM_MachineWhite;
\r
8238 case TwoMachinesPlay:
\r
8239 nowChecked = IDM_TwoMachines;
\r
8242 nowChecked = IDM_AnalysisMode;
\r
8245 nowChecked = IDM_AnalyzeFile;
\r
8248 nowChecked = IDM_EditGame;
\r
8250 case PlayFromGameFile:
\r
8251 nowChecked = IDM_LoadGame;
\r
8253 case EditPosition:
\r
8254 nowChecked = IDM_EditPosition;
\r
8257 nowChecked = IDM_Training;
\r
8259 case IcsPlayingWhite:
\r
8260 case IcsPlayingBlack:
\r
8261 case IcsObserving:
\r
8263 nowChecked = IDM_IcsClient;
\r
8270 if(prevChecked == IDM_TwoMachines) // [HGM] 'Machine Match' might have gotten disabled when stopping match
\r
8271 EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_ENABLED);
\r
8272 CheckMark(prevChecked, MF_UNCHECKED);
\r
8273 CheckMark(nowChecked, MF_CHECKED);
\r
8274 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8276 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8277 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8278 MF_BYCOMMAND|MF_ENABLED);
\r
8280 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8281 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8284 prevChecked = nowChecked;
\r
8286 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8287 if (appData.icsActive) {
\r
8288 if (appData.icsEngineAnalyze) {
\r
8289 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8291 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8294 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8300 HMENU hmenu = GetMenu(hwndMain);
\r
8301 SetMenuEnables(hmenu, icsEnables);
\r
8302 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8303 MF_BYCOMMAND|MF_ENABLED);
\r
8305 if (appData.zippyPlay) {
\r
8306 SetMenuEnables(hmenu, zippyEnables);
\r
8307 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8308 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8309 MF_BYCOMMAND|MF_ENABLED);
\r
8317 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8323 HMENU hmenu = GetMenu(hwndMain);
\r
8324 SetMenuEnables(hmenu, ncpEnables);
\r
8325 DrawMenuBar(hwndMain);
\r
8331 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8335 SetTrainingModeOn()
\r
8338 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8339 for (i = 0; i < N_BUTTONS; i++) {
\r
8340 if (buttonDesc[i].hwnd != NULL)
\r
8341 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8346 VOID SetTrainingModeOff()
\r
8349 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8350 for (i = 0; i < N_BUTTONS; i++) {
\r
8351 if (buttonDesc[i].hwnd != NULL)
\r
8352 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8358 SetUserThinkingEnables()
\r
8360 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8364 SetMachineThinkingEnables()
\r
8366 HMENU hMenu = GetMenu(hwndMain);
\r
8367 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8369 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8371 if (gameMode == MachinePlaysBlack) {
\r
8372 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8373 } else if (gameMode == MachinePlaysWhite) {
\r
8374 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8375 } else if (gameMode == TwoMachinesPlay) {
\r
8376 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8382 DisplayTitle(char *str)
\r
8384 char title[MSG_SIZ], *host;
\r
8385 if (str[0] != NULLCHAR) {
\r
8386 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8387 } else if (appData.icsActive) {
\r
8388 if (appData.icsCommPort[0] != NULLCHAR)
\r
8391 host = appData.icsHost;
\r
8392 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8393 } else if (appData.noChessProgram) {
\r
8394 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8396 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8397 strcat(title, ": ");
\r
8398 strcat(title, first.tidy);
\r
8400 SetWindowText(hwndMain, title);
\r
8405 DisplayMessage(char *str1, char *str2)
\r
8409 int remain = MESSAGE_TEXT_MAX - 1;
\r
8412 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8413 messageText[0] = NULLCHAR;
\r
8415 len = strlen(str1);
\r
8416 if (len > remain) len = remain;
\r
8417 strncpy(messageText, str1, len);
\r
8418 messageText[len] = NULLCHAR;
\r
8421 if (*str2 && remain >= 2) {
\r
8423 strcat(messageText, " ");
\r
8426 len = strlen(str2);
\r
8427 if (len > remain) len = remain;
\r
8428 strncat(messageText, str2, len);
\r
8430 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8431 safeStrCpy(lastMsg, messageText, MSG_SIZ);
\r
8433 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8437 hdc = GetDC(hwndMain);
\r
8438 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8439 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8440 &messageRect, messageText, strlen(messageText), NULL);
\r
8441 (void) SelectObject(hdc, oldFont);
\r
8442 (void) ReleaseDC(hwndMain, hdc);
\r
8446 DisplayError(char *str, int error)
\r
8448 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8452 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8454 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8455 NULL, error, LANG_NEUTRAL,
\r
8456 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8458 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8460 ErrorMap *em = errmap;
\r
8461 while (em->err != 0 && em->err != error) em++;
\r
8462 if (em->err != 0) {
\r
8463 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8465 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8470 ErrorPopUp(_("Error"), buf);
\r
8475 DisplayMoveError(char *str)
\r
8477 fromX = fromY = -1;
\r
8478 ClearHighlights();
\r
8479 DrawPosition(FALSE, NULL);
\r
8480 if (appData.popupMoveErrors) {
\r
8481 ErrorPopUp(_("Error"), str);
\r
8483 DisplayMessage(str, "");
\r
8484 moveErrorMessageUp = TRUE;
\r
8489 DisplayFatalError(char *str, int error, int exitStatus)
\r
8491 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8493 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8496 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8497 NULL, error, LANG_NEUTRAL,
\r
8498 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8500 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8502 ErrorMap *em = errmap;
\r
8503 while (em->err != 0 && em->err != error) em++;
\r
8504 if (em->err != 0) {
\r
8505 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8507 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8512 if (appData.debugMode) {
\r
8513 fprintf(debugFP, "%s: %s\n", label, str);
\r
8515 if (appData.popupExitMessage) {
\r
8516 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8517 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8519 ExitEvent(exitStatus);
\r
8524 DisplayInformation(char *str)
\r
8526 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8531 DisplayNote(char *str)
\r
8533 ErrorPopUp(_("Note"), str);
\r
8538 char *title, *question, *replyPrefix;
\r
8543 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8545 static QuestionParams *qp;
\r
8546 char reply[MSG_SIZ];
\r
8549 switch (message) {
\r
8550 case WM_INITDIALOG:
\r
8551 qp = (QuestionParams *) lParam;
\r
8552 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8553 Translate(hDlg, DLG_Question);
\r
8554 SetWindowText(hDlg, qp->title);
\r
8555 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8556 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8560 switch (LOWORD(wParam)) {
\r
8562 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8563 if (*reply) strcat(reply, " ");
\r
8564 len = strlen(reply);
\r
8565 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8566 strcat(reply, "\n");
\r
8567 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8568 EndDialog(hDlg, TRUE);
\r
8569 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8572 EndDialog(hDlg, FALSE);
\r
8583 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8585 QuestionParams qp;
\r
8589 qp.question = question;
\r
8590 qp.replyPrefix = replyPrefix;
\r
8592 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8593 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8594 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8595 FreeProcInstance(lpProc);
\r
8598 /* [AS] Pick FRC position */
\r
8599 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8601 static int * lpIndexFRC;
\r
8607 case WM_INITDIALOG:
\r
8608 lpIndexFRC = (int *) lParam;
\r
8610 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8611 Translate(hDlg, DLG_NewGameFRC);
\r
8613 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8614 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8615 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8616 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8621 switch( LOWORD(wParam) ) {
\r
8623 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8624 EndDialog( hDlg, 0 );
\r
8625 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8628 EndDialog( hDlg, 1 );
\r
8630 case IDC_NFG_Edit:
\r
8631 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8632 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8634 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8637 case IDC_NFG_Random:
\r
8638 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8639 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8652 int index = appData.defaultFrcPosition;
\r
8653 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8655 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8657 if( result == 0 ) {
\r
8658 appData.defaultFrcPosition = index;
\r
8664 /* [AS] Game list options. Refactored by HGM */
\r
8666 HWND gameListOptionsDialog;
\r
8668 // low-level front-end: clear text edit / list widget
\r
8673 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8676 // low-level front-end: clear text edit / list widget
\r
8678 GLT_DeSelectList()
\r
8680 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8683 // low-level front-end: append line to text edit / list widget
\r
8685 GLT_AddToList( char *name )
\r
8688 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8692 // low-level front-end: get line from text edit / list widget
\r
8694 GLT_GetFromList( int index, char *name )
\r
8697 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8703 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8705 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8706 int idx2 = idx1 + delta;
\r
8707 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8709 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8712 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8713 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8714 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8715 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8719 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8723 case WM_INITDIALOG:
\r
8724 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8726 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8727 Translate(hDlg, DLG_GameListOptions);
\r
8729 /* Initialize list */
\r
8730 GLT_TagsToList( lpUserGLT );
\r
8732 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8737 switch( LOWORD(wParam) ) {
\r
8740 EndDialog( hDlg, 0 );
\r
8743 EndDialog( hDlg, 1 );
\r
8746 case IDC_GLT_Default:
\r
8747 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8750 case IDC_GLT_Restore:
\r
8751 GLT_TagsToList( appData.gameListTags );
\r
8755 GLT_MoveSelection( hDlg, -1 );
\r
8758 case IDC_GLT_Down:
\r
8759 GLT_MoveSelection( hDlg, +1 );
\r
8769 int GameListOptions()
\r
8772 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8774 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8776 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8778 if( result == 0 ) {
\r
8779 char *oldTags = appData.gameListTags;
\r
8780 /* [AS] Memory leak here! */
\r
8781 appData.gameListTags = strdup( lpUserGLT );
\r
8782 if(strcmp(oldTags, appData.gameListTags)) // [HGM] redo Game List when we changed something
\r
8783 GameListToListBox(NULL, TRUE, ".", NULL, FALSE, FALSE); // "." as filter is kludge to select all
\r
8790 DisplayIcsInteractionTitle(char *str)
\r
8792 char consoleTitle[MSG_SIZ];
\r
8794 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8795 SetWindowText(hwndConsole, consoleTitle);
\r
8797 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
8798 char buf[MSG_SIZ], *p = buf, *q;
\r
8799 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
8801 q = strchr(p, ';');
\r
8803 if(*p) ChatPopUp(p);
\r
8807 SetActiveWindow(hwndMain);
\r
8811 DrawPosition(int fullRedraw, Board board)
\r
8813 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8816 void NotifyFrontendLogin()
\r
8819 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8825 fromX = fromY = -1;
\r
8826 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8827 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8828 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8829 dragInfo.lastpos = dragInfo.pos;
\r
8830 dragInfo.start.x = dragInfo.start.y = -1;
\r
8831 dragInfo.from = dragInfo.start;
\r
8833 DrawPosition(TRUE, NULL);
\r
8840 CommentPopUp(char *title, char *str)
\r
8842 HWND hwnd = GetActiveWindow();
\r
8843 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8845 SetActiveWindow(hwnd);
\r
8849 CommentPopDown(void)
\r
8851 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8852 if (commentDialog) {
\r
8853 ShowWindow(commentDialog, SW_HIDE);
\r
8855 commentUp = FALSE;
\r
8859 EditCommentPopUp(int index, char *title, char *str)
\r
8861 EitherCommentPopUp(index, title, str, TRUE);
\r
8868 MyPlaySound(&sounds[(int)SoundRoar]);
\r
8875 MyPlaySound(&sounds[(int)SoundMove]);
\r
8878 VOID PlayIcsWinSound()
\r
8880 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8883 VOID PlayIcsLossSound()
\r
8885 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8888 VOID PlayIcsDrawSound()
\r
8890 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8893 VOID PlayIcsUnfinishedSound()
\r
8895 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8901 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8907 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8915 consoleEcho = TRUE;
\r
8916 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8917 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8918 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8927 consoleEcho = FALSE;
\r
8928 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8929 /* This works OK: set text and background both to the same color */
\r
8931 cf.crTextColor = COLOR_ECHOOFF;
\r
8932 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8933 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8936 /* No Raw()...? */
\r
8938 void Colorize(ColorClass cc, int continuation)
\r
8940 currentColorClass = cc;
\r
8941 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8942 consoleCF.crTextColor = textAttribs[cc].color;
\r
8943 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8944 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8950 static char buf[MSG_SIZ];
\r
8951 DWORD bufsiz = MSG_SIZ;
\r
8953 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8954 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8956 if (!GetUserName(buf, &bufsiz)) {
\r
8957 /*DisplayError("Error getting user name", GetLastError());*/
\r
8958 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8966 static char buf[MSG_SIZ];
\r
8967 DWORD bufsiz = MSG_SIZ;
\r
8969 if (!GetComputerName(buf, &bufsiz)) {
\r
8970 /*DisplayError("Error getting host name", GetLastError());*/
\r
8971 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8978 ClockTimerRunning()
\r
8980 return clockTimerEvent != 0;
\r
8986 if (clockTimerEvent == 0) return FALSE;
\r
8987 KillTimer(hwndMain, clockTimerEvent);
\r
8988 clockTimerEvent = 0;
\r
8993 StartClockTimer(long millisec)
\r
8995 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8996 (UINT) millisec, NULL);
\r
9000 DisplayWhiteClock(long timeRemaining, int highlight)
\r
9003 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
9005 if(appData.noGUI) return;
\r
9006 hdc = GetDC(hwndMain);
\r
9007 if (!IsIconic(hwndMain)) {
\r
9008 DisplayAClock(hdc, timeRemaining, highlight,
\r
9009 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
9011 if (highlight && iconCurrent == iconBlack) {
\r
9012 iconCurrent = iconWhite;
\r
9013 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
9014 if (IsIconic(hwndMain)) {
\r
9015 DrawIcon(hdc, 2, 2, iconCurrent);
\r
9018 (void) ReleaseDC(hwndMain, hdc);
\r
9020 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
9024 DisplayBlackClock(long timeRemaining, int highlight)
\r
9027 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
9030 if(appData.noGUI) return;
\r
9031 hdc = GetDC(hwndMain);
\r
9032 if (!IsIconic(hwndMain)) {
\r
9033 DisplayAClock(hdc, timeRemaining, highlight,
\r
9034 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
9036 if (highlight && iconCurrent == iconWhite) {
\r
9037 iconCurrent = iconBlack;
\r
9038 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
9039 if (IsIconic(hwndMain)) {
\r
9040 DrawIcon(hdc, 2, 2, iconCurrent);
\r
9043 (void) ReleaseDC(hwndMain, hdc);
\r
9045 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
9050 LoadGameTimerRunning()
\r
9052 return loadGameTimerEvent != 0;
\r
9056 StopLoadGameTimer()
\r
9058 if (loadGameTimerEvent == 0) return FALSE;
\r
9059 KillTimer(hwndMain, loadGameTimerEvent);
\r
9060 loadGameTimerEvent = 0;
\r
9065 StartLoadGameTimer(long millisec)
\r
9067 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
9068 (UINT) millisec, NULL);
\r
9076 char fileTitle[MSG_SIZ];
\r
9078 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
9079 f = OpenFileDialog(hwndMain, "a", defName,
\r
9080 appData.oldSaveStyle ? "gam" : "pgn",
\r
9082 _("Save Game to File"), NULL, fileTitle, NULL);
\r
9084 SaveGame(f, 0, "");
\r
9091 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
9093 if (delayedTimerEvent != 0) {
\r
9094 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
9095 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
9097 KillTimer(hwndMain, delayedTimerEvent);
\r
9098 delayedTimerEvent = 0;
\r
9099 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
9100 delayedTimerCallback();
\r
9102 delayedTimerCallback = cb;
\r
9103 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
9104 (UINT) millisec, NULL);
\r
9107 DelayedEventCallback
\r
9110 if (delayedTimerEvent) {
\r
9111 return delayedTimerCallback;
\r
9118 CancelDelayedEvent()
\r
9120 if (delayedTimerEvent) {
\r
9121 KillTimer(hwndMain, delayedTimerEvent);
\r
9122 delayedTimerEvent = 0;
\r
9126 DWORD GetWin32Priority(int nice)
\r
9127 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
9129 REALTIME_PRIORITY_CLASS 0x00000100
\r
9130 HIGH_PRIORITY_CLASS 0x00000080
\r
9131 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
9132 NORMAL_PRIORITY_CLASS 0x00000020
\r
9133 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
9134 IDLE_PRIORITY_CLASS 0x00000040
\r
9136 if (nice < -15) return 0x00000080;
\r
9137 if (nice < 0) return 0x00008000;
\r
9138 if (nice == 0) return 0x00000020;
\r
9139 if (nice < 15) return 0x00004000;
\r
9140 return 0x00000040;
\r
9143 void RunCommand(char *cmdLine)
\r
9145 /* Now create the child process. */
\r
9146 STARTUPINFO siStartInfo;
\r
9147 PROCESS_INFORMATION piProcInfo;
\r
9149 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9150 siStartInfo.lpReserved = NULL;
\r
9151 siStartInfo.lpDesktop = NULL;
\r
9152 siStartInfo.lpTitle = NULL;
\r
9153 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9154 siStartInfo.cbReserved2 = 0;
\r
9155 siStartInfo.lpReserved2 = NULL;
\r
9156 siStartInfo.hStdInput = NULL;
\r
9157 siStartInfo.hStdOutput = NULL;
\r
9158 siStartInfo.hStdError = NULL;
\r
9160 CreateProcess(NULL,
\r
9161 cmdLine, /* command line */
\r
9162 NULL, /* process security attributes */
\r
9163 NULL, /* primary thread security attrs */
\r
9164 TRUE, /* handles are inherited */
\r
9165 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9166 NULL, /* use parent's environment */
\r
9168 &siStartInfo, /* STARTUPINFO pointer */
\r
9169 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9171 CloseHandle(piProcInfo.hThread);
\r
9174 /* Start a child process running the given program.
\r
9175 The process's standard output can be read from "from", and its
\r
9176 standard input can be written to "to".
\r
9177 Exit with fatal error if anything goes wrong.
\r
9178 Returns an opaque pointer that can be used to destroy the process
\r
9182 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
9184 #define BUFSIZE 4096
\r
9186 HANDLE hChildStdinRd, hChildStdinWr,
\r
9187 hChildStdoutRd, hChildStdoutWr;
\r
9188 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
9189 SECURITY_ATTRIBUTES saAttr;
\r
9191 PROCESS_INFORMATION piProcInfo;
\r
9192 STARTUPINFO siStartInfo;
\r
9194 char buf[MSG_SIZ];
\r
9197 if (appData.debugMode) {
\r
9198 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
9203 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
9204 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
9205 saAttr.bInheritHandle = TRUE;
\r
9206 saAttr.lpSecurityDescriptor = NULL;
\r
9209 * The steps for redirecting child's STDOUT:
\r
9210 * 1. Create anonymous pipe to be STDOUT for child.
\r
9211 * 2. Create a noninheritable duplicate of read handle,
\r
9212 * and close the inheritable read handle.
\r
9215 /* Create a pipe for the child's STDOUT. */
\r
9216 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
9217 return GetLastError();
\r
9220 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
9221 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
9222 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
9223 FALSE, /* not inherited */
\r
9224 DUPLICATE_SAME_ACCESS);
\r
9226 return GetLastError();
\r
9228 CloseHandle(hChildStdoutRd);
\r
9231 * The steps for redirecting child's STDIN:
\r
9232 * 1. Create anonymous pipe to be STDIN for child.
\r
9233 * 2. Create a noninheritable duplicate of write handle,
\r
9234 * and close the inheritable write handle.
\r
9237 /* Create a pipe for the child's STDIN. */
\r
9238 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
9239 return GetLastError();
\r
9242 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
9243 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
9244 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
9245 FALSE, /* not inherited */
\r
9246 DUPLICATE_SAME_ACCESS);
\r
9248 return GetLastError();
\r
9250 CloseHandle(hChildStdinWr);
\r
9252 /* Arrange to (1) look in dir for the child .exe file, and
\r
9253 * (2) have dir be the child's working directory. Interpret
\r
9254 * dir relative to the directory WinBoard loaded from. */
\r
9255 GetCurrentDirectory(MSG_SIZ, buf);
\r
9256 SetCurrentDirectory(installDir);
\r
9257 SetCurrentDirectory(dir);
\r
9259 /* Now create the child process. */
\r
9261 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9262 siStartInfo.lpReserved = NULL;
\r
9263 siStartInfo.lpDesktop = NULL;
\r
9264 siStartInfo.lpTitle = NULL;
\r
9265 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9266 siStartInfo.cbReserved2 = 0;
\r
9267 siStartInfo.lpReserved2 = NULL;
\r
9268 siStartInfo.hStdInput = hChildStdinRd;
\r
9269 siStartInfo.hStdOutput = hChildStdoutWr;
\r
9270 siStartInfo.hStdError = hChildStdoutWr;
\r
9272 fSuccess = CreateProcess(NULL,
\r
9273 cmdLine, /* command line */
\r
9274 NULL, /* process security attributes */
\r
9275 NULL, /* primary thread security attrs */
\r
9276 TRUE, /* handles are inherited */
\r
9277 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9278 NULL, /* use parent's environment */
\r
9280 &siStartInfo, /* STARTUPINFO pointer */
\r
9281 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9283 err = GetLastError();
\r
9284 SetCurrentDirectory(buf); /* return to prev directory */
\r
9289 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
9290 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
9291 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
9294 /* Close the handles we don't need in the parent */
\r
9295 CloseHandle(piProcInfo.hThread);
\r
9296 CloseHandle(hChildStdinRd);
\r
9297 CloseHandle(hChildStdoutWr);
\r
9299 /* Prepare return value */
\r
9300 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9301 cp->kind = CPReal;
\r
9302 cp->hProcess = piProcInfo.hProcess;
\r
9303 cp->pid = piProcInfo.dwProcessId;
\r
9304 cp->hFrom = hChildStdoutRdDup;
\r
9305 cp->hTo = hChildStdinWrDup;
\r
9307 *pr = (void *) cp;
\r
9309 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
9310 2000 where engines sometimes don't see the initial command(s)
\r
9311 from WinBoard and hang. I don't understand how that can happen,
\r
9312 but the Sleep is harmless, so I've put it in. Others have also
\r
9313 reported what may be the same problem, so hopefully this will fix
\r
9314 it for them too. */
\r
9322 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
9324 ChildProc *cp; int result;
\r
9326 cp = (ChildProc *) pr;
\r
9327 if (cp == NULL) return;
\r
9329 switch (cp->kind) {
\r
9331 /* TerminateProcess is considered harmful, so... */
\r
9332 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9333 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9334 /* The following doesn't work because the chess program
\r
9335 doesn't "have the same console" as WinBoard. Maybe
\r
9336 we could arrange for this even though neither WinBoard
\r
9337 nor the chess program uses a console for stdio? */
\r
9338 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9340 /* [AS] Special termination modes for misbehaving programs... */
\r
9341 if( signal & 8 ) {
\r
9342 result = TerminateProcess( cp->hProcess, 0 );
\r
9344 if ( appData.debugMode) {
\r
9345 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9348 else if( signal & 4 ) {
\r
9349 DWORD dw = WaitForSingleObject( cp->hProcess, appData.delayAfterQuit*1000 + 50 ); // Wait 3 seconds at most
\r
9351 if( dw != WAIT_OBJECT_0 ) {
\r
9352 result = TerminateProcess( cp->hProcess, 0 );
\r
9354 if ( appData.debugMode) {
\r
9355 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9361 CloseHandle(cp->hProcess);
\r
9365 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9369 closesocket(cp->sock);
\r
9374 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9375 closesocket(cp->sock);
\r
9376 closesocket(cp->sock2);
\r
9384 InterruptChildProcess(ProcRef pr)
\r
9388 cp = (ChildProc *) pr;
\r
9389 if (cp == NULL) return;
\r
9390 switch (cp->kind) {
\r
9392 /* The following doesn't work because the chess program
\r
9393 doesn't "have the same console" as WinBoard. Maybe
\r
9394 we could arrange for this even though neither WinBoard
\r
9395 nor the chess program uses a console for stdio */
\r
9396 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9401 /* Can't interrupt */
\r
9405 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9412 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9414 char cmdLine[MSG_SIZ];
\r
9416 if (port[0] == NULLCHAR) {
\r
9417 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9419 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9421 return StartChildProcess(cmdLine, "", pr);
\r
9425 /* Code to open TCP sockets */
\r
9428 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9434 struct sockaddr_in sa, mysa;
\r
9435 struct hostent FAR *hp;
\r
9436 unsigned short uport;
\r
9437 WORD wVersionRequested;
\r
9440 /* Initialize socket DLL */
\r
9441 wVersionRequested = MAKEWORD(1, 1);
\r
9442 err = WSAStartup(wVersionRequested, &wsaData);
\r
9443 if (err != 0) return err;
\r
9446 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9447 err = WSAGetLastError();
\r
9452 /* Bind local address using (mostly) don't-care values.
\r
9454 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9455 mysa.sin_family = AF_INET;
\r
9456 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9457 uport = (unsigned short) 0;
\r
9458 mysa.sin_port = htons(uport);
\r
9459 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9460 == SOCKET_ERROR) {
\r
9461 err = WSAGetLastError();
\r
9466 /* Resolve remote host name */
\r
9467 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9468 if (!(hp = gethostbyname(host))) {
\r
9469 unsigned int b0, b1, b2, b3;
\r
9471 err = WSAGetLastError();
\r
9473 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9474 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9475 hp->h_addrtype = AF_INET;
\r
9477 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9478 hp->h_addr_list[0] = (char *) malloc(4);
\r
9479 hp->h_addr_list[0][0] = (char) b0;
\r
9480 hp->h_addr_list[0][1] = (char) b1;
\r
9481 hp->h_addr_list[0][2] = (char) b2;
\r
9482 hp->h_addr_list[0][3] = (char) b3;
\r
9488 sa.sin_family = hp->h_addrtype;
\r
9489 uport = (unsigned short) atoi(port);
\r
9490 sa.sin_port = htons(uport);
\r
9491 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9493 /* Make connection */
\r
9494 if (connect(s, (struct sockaddr *) &sa,
\r
9495 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9496 err = WSAGetLastError();
\r
9501 /* Prepare return value */
\r
9502 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9503 cp->kind = CPSock;
\r
9505 *pr = (ProcRef *) cp;
\r
9511 OpenCommPort(char *name, ProcRef *pr)
\r
9516 char fullname[MSG_SIZ];
\r
9518 if (*name != '\\')
\r
9519 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9521 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9523 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9524 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9525 if (h == (HANDLE) -1) {
\r
9526 return GetLastError();
\r
9530 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9532 /* Accumulate characters until a 100ms pause, then parse */
\r
9533 ct.ReadIntervalTimeout = 100;
\r
9534 ct.ReadTotalTimeoutMultiplier = 0;
\r
9535 ct.ReadTotalTimeoutConstant = 0;
\r
9536 ct.WriteTotalTimeoutMultiplier = 0;
\r
9537 ct.WriteTotalTimeoutConstant = 0;
\r
9538 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9540 /* Prepare return value */
\r
9541 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9542 cp->kind = CPComm;
\r
9545 *pr = (ProcRef *) cp;
\r
9551 OpenLoopback(ProcRef *pr)
\r
9553 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9559 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9564 struct sockaddr_in sa, mysa;
\r
9565 struct hostent FAR *hp;
\r
9566 unsigned short uport;
\r
9567 WORD wVersionRequested;
\r
9570 char stderrPortStr[MSG_SIZ];
\r
9572 /* Initialize socket DLL */
\r
9573 wVersionRequested = MAKEWORD(1, 1);
\r
9574 err = WSAStartup(wVersionRequested, &wsaData);
\r
9575 if (err != 0) return err;
\r
9577 /* Resolve remote host name */
\r
9578 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9579 if (!(hp = gethostbyname(host))) {
\r
9580 unsigned int b0, b1, b2, b3;
\r
9582 err = WSAGetLastError();
\r
9584 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9585 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9586 hp->h_addrtype = AF_INET;
\r
9588 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9589 hp->h_addr_list[0] = (char *) malloc(4);
\r
9590 hp->h_addr_list[0][0] = (char) b0;
\r
9591 hp->h_addr_list[0][1] = (char) b1;
\r
9592 hp->h_addr_list[0][2] = (char) b2;
\r
9593 hp->h_addr_list[0][3] = (char) b3;
\r
9599 sa.sin_family = hp->h_addrtype;
\r
9600 uport = (unsigned short) 514;
\r
9601 sa.sin_port = htons(uport);
\r
9602 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9604 /* Bind local socket to unused "privileged" port address
\r
9606 s = INVALID_SOCKET;
\r
9607 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9608 mysa.sin_family = AF_INET;
\r
9609 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9610 for (fromPort = 1023;; fromPort--) {
\r
9611 if (fromPort < 0) {
\r
9613 return WSAEADDRINUSE;
\r
9615 if (s == INVALID_SOCKET) {
\r
9616 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9617 err = WSAGetLastError();
\r
9622 uport = (unsigned short) fromPort;
\r
9623 mysa.sin_port = htons(uport);
\r
9624 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9625 == SOCKET_ERROR) {
\r
9626 err = WSAGetLastError();
\r
9627 if (err == WSAEADDRINUSE) continue;
\r
9631 if (connect(s, (struct sockaddr *) &sa,
\r
9632 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9633 err = WSAGetLastError();
\r
9634 if (err == WSAEADDRINUSE) {
\r
9645 /* Bind stderr local socket to unused "privileged" port address
\r
9647 s2 = INVALID_SOCKET;
\r
9648 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9649 mysa.sin_family = AF_INET;
\r
9650 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9651 for (fromPort = 1023;; fromPort--) {
\r
9652 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9653 if (fromPort < 0) {
\r
9654 (void) closesocket(s);
\r
9656 return WSAEADDRINUSE;
\r
9658 if (s2 == INVALID_SOCKET) {
\r
9659 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9660 err = WSAGetLastError();
\r
9666 uport = (unsigned short) fromPort;
\r
9667 mysa.sin_port = htons(uport);
\r
9668 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9669 == SOCKET_ERROR) {
\r
9670 err = WSAGetLastError();
\r
9671 if (err == WSAEADDRINUSE) continue;
\r
9672 (void) closesocket(s);
\r
9676 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9677 err = WSAGetLastError();
\r
9678 if (err == WSAEADDRINUSE) {
\r
9680 s2 = INVALID_SOCKET;
\r
9683 (void) closesocket(s);
\r
9684 (void) closesocket(s2);
\r
9690 prevStderrPort = fromPort; // remember port used
\r
9691 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9693 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9694 err = WSAGetLastError();
\r
9695 (void) closesocket(s);
\r
9696 (void) closesocket(s2);
\r
9701 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9702 err = WSAGetLastError();
\r
9703 (void) closesocket(s);
\r
9704 (void) closesocket(s2);
\r
9708 if (*user == NULLCHAR) user = UserName();
\r
9709 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9710 err = WSAGetLastError();
\r
9711 (void) closesocket(s);
\r
9712 (void) closesocket(s2);
\r
9716 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9717 err = WSAGetLastError();
\r
9718 (void) closesocket(s);
\r
9719 (void) closesocket(s2);
\r
9724 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9725 err = WSAGetLastError();
\r
9726 (void) closesocket(s);
\r
9727 (void) closesocket(s2);
\r
9731 (void) closesocket(s2); /* Stop listening */
\r
9733 /* Prepare return value */
\r
9734 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9735 cp->kind = CPRcmd;
\r
9738 *pr = (ProcRef *) cp;
\r
9745 AddInputSource(ProcRef pr, int lineByLine,
\r
9746 InputCallback func, VOIDSTAR closure)
\r
9748 InputSource *is, *is2 = NULL;
\r
9749 ChildProc *cp = (ChildProc *) pr;
\r
9751 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9752 is->lineByLine = lineByLine;
\r
9754 is->closure = closure;
\r
9755 is->second = NULL;
\r
9756 is->next = is->buf;
\r
9757 if (pr == NoProc) {
\r
9758 is->kind = CPReal;
\r
9759 consoleInputSource = is;
\r
9761 is->kind = cp->kind;
\r
9763 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9764 we create all threads suspended so that the is->hThread variable can be
\r
9765 safely assigned, then let the threads start with ResumeThread.
\r
9767 switch (cp->kind) {
\r
9769 is->hFile = cp->hFrom;
\r
9770 cp->hFrom = NULL; /* now owned by InputThread */
\r
9772 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9773 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9777 is->hFile = cp->hFrom;
\r
9778 cp->hFrom = NULL; /* now owned by InputThread */
\r
9780 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9781 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9785 is->sock = cp->sock;
\r
9787 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9788 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9792 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9794 is->sock = cp->sock;
\r
9796 is2->sock = cp->sock2;
\r
9797 is2->second = is2;
\r
9799 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9800 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9802 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9803 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9807 if( is->hThread != NULL ) {
\r
9808 ResumeThread( is->hThread );
\r
9811 if( is2 != NULL && is2->hThread != NULL ) {
\r
9812 ResumeThread( is2->hThread );
\r
9816 return (InputSourceRef) is;
\r
9820 RemoveInputSource(InputSourceRef isr)
\r
9824 is = (InputSource *) isr;
\r
9825 is->hThread = NULL; /* tell thread to stop */
\r
9826 CloseHandle(is->hThread);
\r
9827 if (is->second != NULL) {
\r
9828 is->second->hThread = NULL;
\r
9829 CloseHandle(is->second->hThread);
\r
9833 int no_wrap(char *message, int count)
\r
9835 ConsoleOutput(message, count, FALSE);
\r
9840 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9843 int outCount = SOCKET_ERROR;
\r
9844 ChildProc *cp = (ChildProc *) pr;
\r
9845 static OVERLAPPED ovl;
\r
9847 static int line = 0;
\r
9851 if (appData.noJoin || !appData.useInternalWrap)
\r
9852 return no_wrap(message, count);
\r
9855 int width = get_term_width();
\r
9856 int len = wrap(NULL, message, count, width, &line);
\r
9857 char *msg = malloc(len);
\r
9861 return no_wrap(message, count);
\r
9864 dbgchk = wrap(msg, message, count, width, &line);
\r
9865 if (dbgchk != len && appData.debugMode)
\r
9866 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9867 ConsoleOutput(msg, len, FALSE);
\r
9874 if (ovl.hEvent == NULL) {
\r
9875 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9877 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9879 switch (cp->kind) {
\r
9882 outCount = send(cp->sock, message, count, 0);
\r
9883 if (outCount == SOCKET_ERROR) {
\r
9884 *outError = WSAGetLastError();
\r
9886 *outError = NO_ERROR;
\r
9891 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9892 &dOutCount, NULL)) {
\r
9893 *outError = NO_ERROR;
\r
9894 outCount = (int) dOutCount;
\r
9896 *outError = GetLastError();
\r
9901 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9902 &dOutCount, &ovl);
\r
9903 if (*outError == NO_ERROR) {
\r
9904 outCount = (int) dOutCount;
\r
9914 if(n != 0) Sleep(n);
\r
9918 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9921 /* Ignore delay, not implemented for WinBoard */
\r
9922 return OutputToProcess(pr, message, count, outError);
\r
9927 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9928 char *buf, int count, int error)
\r
9930 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9933 /* see wgamelist.c for Game List functions */
\r
9934 /* see wedittags.c for Edit Tags functions */
\r
9941 char buf[MSG_SIZ];
\r
9944 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9945 f = fopen(buf, "r");
\r
9947 ProcessICSInitScript(f);
\r
9957 StartAnalysisClock()
\r
9959 if (analysisTimerEvent) return;
\r
9960 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9961 (UINT) 2000, NULL);
\r
9965 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9967 highlightInfo.sq[0].x = fromX;
\r
9968 highlightInfo.sq[0].y = fromY;
\r
9969 highlightInfo.sq[1].x = toX;
\r
9970 highlightInfo.sq[1].y = toY;
\r
9976 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9977 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9981 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9983 premoveHighlightInfo.sq[0].x = fromX;
\r
9984 premoveHighlightInfo.sq[0].y = fromY;
\r
9985 premoveHighlightInfo.sq[1].x = toX;
\r
9986 premoveHighlightInfo.sq[1].y = toY;
\r
9990 ClearPremoveHighlights()
\r
9992 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9993 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9997 ShutDownFrontEnd()
\r
9999 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
10000 DeleteClipboardTempFiles();
\r
10006 if (IsIconic(hwndMain))
\r
10007 ShowWindow(hwndMain, SW_RESTORE);
\r
10009 SetActiveWindow(hwndMain);
\r
10013 * Prototypes for animation support routines
\r
10015 static void ScreenSquare(int column, int row, POINT * pt);
\r
10016 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
10017 POINT frames[], int * nFrames);
\r
10020 #define kFactor 4
\r
10023 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
10024 { // [HGM] atomic: animate blast wave
\r
10027 explodeInfo.fromX = fromX;
\r
10028 explodeInfo.fromY = fromY;
\r
10029 explodeInfo.toX = toX;
\r
10030 explodeInfo.toY = toY;
\r
10031 for(i=1; i<4*kFactor; i++) {
\r
10032 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
10033 DrawPosition(FALSE, board);
\r
10034 Sleep(appData.animSpeed);
\r
10036 explodeInfo.radius = 0;
\r
10037 DrawPosition(TRUE, board);
\r
10041 AnimateMove(board, fromX, fromY, toX, toY)
\r
10048 ChessSquare piece;
\r
10049 int x = toX, y = toY;
\r
10050 POINT start, finish, mid;
\r
10051 POINT frames[kFactor * 2 + 1];
\r
10054 if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();
\r
10056 if (!appData.animate) return;
\r
10057 if (doingSizing) return;
\r
10058 if (fromY < 0 || fromX < 0) return;
\r
10059 piece = board[fromY][fromX];
\r
10060 if (piece >= EmptySquare) return;
\r
10062 if(killX >= 0) toX = killX, toY = killY; // [HGM] lion: first to kill square
\r
10066 ScreenSquare(fromX, fromY, &start);
\r
10067 ScreenSquare(toX, toY, &finish);
\r
10069 /* All moves except knight jumps move in straight line */
\r
10070 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
10071 mid.x = start.x + (finish.x - start.x) / 2;
\r
10072 mid.y = start.y + (finish.y - start.y) / 2;
\r
10074 /* Knight: make straight movement then diagonal */
\r
10075 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
10076 mid.x = start.x + (finish.x - start.x) / 2;
\r
10080 mid.y = start.y + (finish.y - start.y) / 2;
\r
10084 /* Don't use as many frames for very short moves */
\r
10085 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
10086 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
10088 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
10090 animInfo.from.x = fromX;
\r
10091 animInfo.from.y = fromY;
\r
10092 animInfo.to.x = toX;
\r
10093 animInfo.to.y = toY;
\r
10094 animInfo.lastpos = start;
\r
10095 animInfo.piece = piece;
\r
10096 for (n = 0; n < nFrames; n++) {
\r
10097 animInfo.pos = frames[n];
\r
10098 DrawPosition(FALSE, NULL);
\r
10099 animInfo.lastpos = animInfo.pos;
\r
10100 Sleep(appData.animSpeed);
\r
10102 animInfo.pos = finish;
\r
10103 DrawPosition(FALSE, NULL);
\r
10105 if(toX != x || toY != y) { fromX = toX; fromY = toY; toX = x; toY = y; goto again; } // second leg
\r
10107 animInfo.piece = EmptySquare;
\r
10108 Explode(board, fromX, fromY, toX, toY);
\r
10111 /* Convert board position to corner of screen rect and color */
\r
10114 ScreenSquare(column, row, pt)
\r
10115 int column; int row; POINT * pt;
\r
10118 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
10119 pt->y = lineGap + row * (squareSize + lineGap) + border;
\r
10121 pt->x = lineGap + column * (squareSize + lineGap) + border;
\r
10122 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
10126 /* Generate a series of frame coords from start->mid->finish.
\r
10127 The movement rate doubles until the half way point is
\r
10128 reached, then halves back down to the final destination,
\r
10129 which gives a nice slow in/out effect. The algorithmn
\r
10130 may seem to generate too many intermediates for short
\r
10131 moves, but remember that the purpose is to attract the
\r
10132 viewers attention to the piece about to be moved and
\r
10133 then to where it ends up. Too few frames would be less
\r
10137 Tween(start, mid, finish, factor, frames, nFrames)
\r
10138 POINT * start; POINT * mid;
\r
10139 POINT * finish; int factor;
\r
10140 POINT frames[]; int * nFrames;
\r
10142 int n, fraction = 1, count = 0;
\r
10144 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
10145 for (n = 0; n < factor; n++)
\r
10147 for (n = 0; n < factor; n++) {
\r
10148 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
10149 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
10151 fraction = fraction / 2;
\r
10155 frames[count] = *mid;
\r
10158 /* Slow out, stepping 1/2, then 1/4, ... */
\r
10160 for (n = 0; n < factor; n++) {
\r
10161 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
10162 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
10164 fraction = fraction * 2;
\r
10166 *nFrames = count;
\r
10170 SettingsPopUp(ChessProgramState *cps)
\r
10171 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
10172 EngineOptionsPopup(savedHwnd, cps);
\r
10175 int flock(int fid, int code)
\r
10177 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
10179 ov.hEvent = NULL;
\r
10181 ov.OffsetHigh = 0;
\r
10183 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
10185 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
10186 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
10187 default: return -1;
\r
10196 static char col[8][20];
\r
10197 COLORREF color = *(COLORREF *) colorVariable[n];
\r
10199 snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
10204 ActivateTheme (int new)
\r
10205 { // Redo initialization of features depending on options that can occur in themes
\r
10207 if(new) InitDrawingColors();
\r
10208 fontBitmapSquareSize = 0; // request creation of new font pieces
\r
10209 InitDrawingSizes(boardSize, 0);
\r
10210 InvalidateRect(hwndMain, NULL, TRUE);
\r