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
3758 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3760 static Board lastReq[2], lastDrawn[2];
\r
3761 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3762 static int lastDrawnFlipView = 0;
\r
3763 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3764 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3767 HBITMAP bufferBitmap;
\r
3768 HBITMAP oldBitmap;
\r
3770 HRGN clips[MAX_CLIPS];
\r
3771 ChessSquare dragged_piece = EmptySquare;
\r
3772 int nr = twoBoards*partnerUp;
\r
3774 /* I'm undecided on this - this function figures out whether a full
\r
3775 * repaint is necessary on its own, so there's no real reason to have the
\r
3776 * caller tell it that. I think this can safely be set to FALSE - but
\r
3777 * if we trust the callers not to request full repaints unnessesarily, then
\r
3778 * we could skip some clipping work. In other words, only request a full
\r
3779 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3780 * gamestart and similar) --Hawk
\r
3782 Boolean fullrepaint = repaint;
\r
3784 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3786 if( DrawPositionNeedsFullRepaint() ) {
\r
3787 fullrepaint = TRUE;
\r
3790 if (board == NULL) {
\r
3791 if (!lastReqValid[nr]) {
\r
3794 board = lastReq[nr];
\r
3796 CopyBoard(lastReq[nr], board);
\r
3797 lastReqValid[nr] = 1;
\r
3800 if (doingSizing) {
\r
3804 if (IsIconic(hwndMain)) {
\r
3808 if (hdc == NULL) {
\r
3809 hdc = GetDC(hwndMain);
\r
3810 if (!appData.monoMode) {
\r
3811 SelectPalette(hdc, hPal, FALSE);
\r
3812 RealizePalette(hdc);
\r
3816 releaseDC = FALSE;
\r
3819 /* Create some work-DCs */
\r
3820 hdcmem = CreateCompatibleDC(hdc);
\r
3821 tmphdc = CreateCompatibleDC(hdc);
\r
3823 /* If dragging is in progress, we temporarely remove the piece */
\r
3824 /* [HGM] or temporarily decrease count if stacked */
\r
3825 /* !! Moved to before board compare !! */
\r
3826 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3827 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3828 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3829 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3830 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3832 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3833 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3834 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3836 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3839 /* Figure out which squares need updating by comparing the
\r
3840 * newest board with the last drawn board and checking if
\r
3841 * flipping has changed.
\r
3843 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3844 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3845 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3846 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3847 SquareToPos(row, column, &x, &y);
\r
3848 clips[num_clips++] =
\r
3849 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3853 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3854 for (i=0; i<2; i++) {
\r
3855 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3856 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3857 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3858 lastDrawnHighlight.sq[i].y >= 0) {
\r
3859 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3860 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3861 clips[num_clips++] =
\r
3862 CreateRectRgn(x - lineGap, y - lineGap,
\r
3863 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3865 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3866 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3867 clips[num_clips++] =
\r
3868 CreateRectRgn(x - lineGap, y - lineGap,
\r
3869 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3873 for (i=0; i<2; i++) {
\r
3874 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3875 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3876 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3877 lastDrawnPremove.sq[i].y >= 0) {
\r
3878 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3879 lastDrawnPremove.sq[i].x, &x, &y);
\r
3880 clips[num_clips++] =
\r
3881 CreateRectRgn(x - lineGap, y - lineGap,
\r
3882 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3884 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3885 premoveHighlightInfo.sq[i].y >= 0) {
\r
3886 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3887 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3888 clips[num_clips++] =
\r
3889 CreateRectRgn(x - lineGap, y - lineGap,
\r
3890 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3894 } else { // nr == 1
\r
3895 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3896 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3897 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3898 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3899 for (i=0; i<2; i++) {
\r
3900 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3901 partnerHighlightInfo.sq[i].y >= 0) {
\r
3902 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3903 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3904 clips[num_clips++] =
\r
3905 CreateRectRgn(x - lineGap, y - lineGap,
\r
3906 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3908 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3909 oldPartnerHighlight.sq[i].y >= 0) {
\r
3910 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3911 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3912 clips[num_clips++] =
\r
3913 CreateRectRgn(x - lineGap, y - lineGap,
\r
3914 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3919 fullrepaint = TRUE;
\r
3922 /* Create a buffer bitmap - this is the actual bitmap
\r
3923 * being written to. When all the work is done, we can
\r
3924 * copy it to the real DC (the screen). This avoids
\r
3925 * the problems with flickering.
\r
3927 GetClientRect(hwndMain, &Rect);
\r
3928 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3929 Rect.bottom-Rect.top+1);
\r
3930 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3931 if (!appData.monoMode) {
\r
3932 SelectPalette(hdcmem, hPal, FALSE);
\r
3935 /* Create clips for dragging */
\r
3936 if (!fullrepaint) {
\r
3937 if (dragInfo.from.x >= 0) {
\r
3938 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3939 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3941 if (dragInfo.start.x >= 0) {
\r
3942 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3943 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3945 if (dragInfo.pos.x >= 0) {
\r
3946 x = dragInfo.pos.x - squareSize / 2;
\r
3947 y = dragInfo.pos.y - squareSize / 2;
\r
3948 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3950 if (dragInfo.lastpos.x >= 0) {
\r
3951 x = dragInfo.lastpos.x - squareSize / 2;
\r
3952 y = dragInfo.lastpos.y - squareSize / 2;
\r
3953 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3957 /* Are we animating a move?
\r
3959 * - remove the piece from the board (temporarely)
\r
3960 * - calculate the clipping region
\r
3962 if (!fullrepaint) {
\r
3963 if (animInfo.piece != EmptySquare) {
\r
3964 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3965 x = boardRect.left + animInfo.lastpos.x;
\r
3966 y = boardRect.top + animInfo.lastpos.y;
\r
3967 x2 = boardRect.left + animInfo.pos.x;
\r
3968 y2 = boardRect.top + animInfo.pos.y;
\r
3969 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3970 /* Slight kludge. The real problem is that after AnimateMove is
\r
3971 done, the position on the screen does not match lastDrawn.
\r
3972 This currently causes trouble only on e.p. captures in
\r
3973 atomic, where the piece moves to an empty square and then
\r
3974 explodes. The old and new positions both had an empty square
\r
3975 at the destination, but animation has drawn a piece there and
\r
3976 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3977 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3981 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3982 if (num_clips == 0)
\r
3983 fullrepaint = TRUE;
\r
3985 /* Set clipping on the memory DC */
\r
3986 if (!fullrepaint) {
\r
3987 SelectClipRgn(hdcmem, clips[0]);
\r
3988 for (x = 1; x < num_clips; x++) {
\r
3989 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3990 abort(); // this should never ever happen!
\r
3994 /* Do all the drawing to the memory DC */
\r
3995 if(explodeInfo.radius) { // [HGM] atomic
\r
3997 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3998 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3999 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
4000 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
4001 x += squareSize/2;
\r
4002 y += squareSize/2;
\r
4003 if(!fullrepaint) {
\r
4004 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
4005 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
4007 DrawGridOnDC(hdcmem);
\r
4008 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
4009 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
4010 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
4011 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
4012 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
4013 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
4014 SelectObject(hdcmem, oldBrush);
\r
4016 if(border) DrawBackgroundOnDC(hdcmem);
\r
4017 DrawGridOnDC(hdcmem);
\r
4018 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
4019 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
4020 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
4022 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
4023 oldPartnerHighlight = partnerHighlightInfo;
\r
4025 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
4027 if(nr == 0) // [HGM] dual: markers only on left board
\r
4028 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
4029 for (column = 0; column < BOARD_WIDTH; column++) {
\r
4030 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
4031 HBRUSH oldBrush = SelectObject(hdcmem, markerBrush[marker[row][column]-1]);
\r
4032 SquareToPos(row, column, &x, &y);
\r
4033 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
4034 x + 3*squareSize/4, y + 3*squareSize/4);
\r
4035 SelectObject(hdcmem, oldBrush);
\r
4040 if( appData.highlightMoveWithArrow ) {
\r
4042 DrawArrowHighlight(hdcmem);
\r
4045 DrawCoordsOnDC(hdcmem);
\r
4047 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
4048 /* to make sure lastDrawn contains what is actually drawn */
\r
4050 /* Put the dragged piece back into place and draw it (out of place!) */
\r
4051 if (dragged_piece != EmptySquare) {
\r
4052 /* [HGM] or restack */
\r
4053 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
4054 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
4056 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
4057 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
4059 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
4060 x = dragInfo.pos.x - squareSize / 2;
\r
4061 y = dragInfo.pos.y - squareSize / 2;
\r
4062 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
4063 ((int) dragInfo.piece < (int) BlackPawn),
\r
4064 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
4067 /* Put the animated piece back into place and draw it */
\r
4068 if (animInfo.piece != EmptySquare) {
\r
4069 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
4070 x = boardRect.left + animInfo.pos.x;
\r
4071 y = boardRect.top + animInfo.pos.y;
\r
4072 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
4073 ((int) animInfo.piece < (int) BlackPawn),
\r
4074 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
4077 /* Release the bufferBitmap by selecting in the old bitmap
\r
4078 * and delete the memory DC
\r
4080 SelectObject(hdcmem, oldBitmap);
\r
4083 /* Set clipping on the target DC */
\r
4084 if (!fullrepaint) {
\r
4085 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
4087 GetRgnBox(clips[x], &rect);
\r
4088 DeleteObject(clips[x]);
\r
4089 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
4090 rect.right + wpMain.width/2, rect.bottom);
\r
4092 SelectClipRgn(hdc, clips[0]);
\r
4093 for (x = 1; x < num_clips; x++) {
\r
4094 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
4095 abort(); // this should never ever happen!
\r
4099 /* Copy the new bitmap onto the screen in one go.
\r
4100 * This way we avoid any flickering
\r
4102 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
4103 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
4104 boardRect.right - boardRect.left,
\r
4105 boardRect.bottom - boardRect.top,
\r
4106 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
4107 if(saveDiagFlag) {
\r
4108 BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData;
\r
4109 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
4110 HBITMAP src = bufferBitmap, obmp; HDC tmp = CreateCompatibleDC(hdc);
\r
4112 bufferBitmap = CreateCompatibleBitmap(hdc, boardRect.right-boardRect.left, Rect.bottom-Rect.top-2*OUTER_MARGIN);
\r
4113 obmp = SelectObject(tmp, bufferBitmap);
\r
4114 BitBlt(tmp, 0, 0, boardRect.right - boardRect.left, Rect.bottom - Rect.top - 2*OUTER_MARGIN,
\r
4115 tmphdc, boardRect.left, OUTER_MARGIN, SRCCOPY);
\r
4116 GetObject(bufferBitmap, sizeof(b), &b);
\r
4117 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\r
4118 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
4119 bih.biWidth = b.bmWidth;
\r
4120 bih.biHeight = b.bmHeight;
\r
4122 bih.biBitCount = b.bmBitsPixel;
\r
4123 bih.biCompression = 0;
\r
4124 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
4125 bih.biXPelsPerMeter = 0;
\r
4126 bih.biYPelsPerMeter = 0;
\r
4127 bih.biClrUsed = 0;
\r
4128 bih.biClrImportant = 0;
\r
4129 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
4130 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
4131 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
4132 // fprintf(diagFile, "%8x\n", (int) pData);
\r
4134 wb = b.bmWidthBytes;
\r
4136 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
4137 int k = ((int*) pData)[i];
\r
4138 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4139 if(j >= 16) break;
\r
4141 if(j >= nrColors) nrColors = j+1;
\r
4143 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
4145 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
4146 for(w=0; w<(wb>>2); w+=2) {
\r
4147 int k = ((int*) pData)[(wb*i>>2) + w];
\r
4148 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4149 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
4150 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
4151 pData[p++] = m | j<<4;
\r
4153 while(p&3) pData[p++] = 0;
\r
4156 wb = ((wb+31)>>5)<<2;
\r
4158 // write BITMAPFILEHEADER
\r
4159 fprintf(diagFile, "BM");
\r
4160 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
4161 fputDW(diagFile, 0);
\r
4162 fputDW(diagFile, 0x36 + (fac?64:0));
\r
4163 // write BITMAPINFOHEADER
\r
4164 fputDW(diagFile, 40);
\r
4165 fputDW(diagFile, b.bmWidth);
\r
4166 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
4167 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
4168 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\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 fputDW(diagFile, 0);
\r
4175 // write color table
\r
4177 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
4178 // write bitmap data
\r
4179 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
4180 fputc(pData[i], diagFile);
\r
4183 DeleteObject(bufferBitmap); bufferBitmap = src;
\r
4184 SelectObject(tmp, obmp);
\r
4188 SelectObject(tmphdc, oldBitmap);
\r
4190 /* Massive cleanup */
\r
4191 for (x = 0; x < num_clips; x++)
\r
4192 DeleteObject(clips[x]);
\r
4195 DeleteObject(bufferBitmap);
\r
4198 ReleaseDC(hwndMain, hdc);
\r
4200 if (lastDrawnFlipView != flipView && nr == 0) {
\r
4202 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
4204 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
4207 /* CopyBoard(lastDrawn, board);*/
\r
4208 lastDrawnHighlight = highlightInfo;
\r
4209 lastDrawnPremove = premoveHighlightInfo;
\r
4210 lastDrawnFlipView = flipView;
\r
4211 lastDrawnValid[nr] = 1;
\r
4214 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
4219 saveDiagFlag = 1; diagFile = f;
\r
4220 HDCDrawPosition(NULL, TRUE, NULL);
\r
4228 /*---------------------------------------------------------------------------*\
\r
4229 | CLIENT PAINT PROCEDURE
\r
4230 | This is the main event-handler for the WM_PAINT message.
\r
4232 \*---------------------------------------------------------------------------*/
\r
4234 PaintProc(HWND hwnd)
\r
4240 if((hdc = BeginPaint(hwnd, &ps))) {
\r
4241 if (IsIconic(hwnd)) {
\r
4242 DrawIcon(hdc, 2, 2, iconCurrent);
\r
4244 if (!appData.monoMode) {
\r
4245 SelectPalette(hdc, hPal, FALSE);
\r
4246 RealizePalette(hdc);
\r
4248 HDCDrawPosition(hdc, 1, NULL);
\r
4249 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
4250 flipView = !flipView; partnerUp = !partnerUp;
\r
4251 HDCDrawPosition(hdc, 1, NULL);
\r
4252 flipView = !flipView; partnerUp = !partnerUp;
\r
4255 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
4256 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
4257 ETO_CLIPPED|ETO_OPAQUE,
\r
4258 &messageRect, messageText, strlen(messageText), NULL);
\r
4259 SelectObject(hdc, oldFont);
\r
4260 DisplayBothClocks();
\r
4263 EndPaint(hwnd,&ps);
\r
4271 * If the user selects on a border boundary, return -1; if off the board,
\r
4272 * return -2. Otherwise map the event coordinate to the square.
\r
4273 * The offset boardRect.left or boardRect.top must already have been
\r
4274 * subtracted from x.
\r
4276 int EventToSquare(x, limit)
\r
4281 if (x < lineGap + border)
\r
4283 x -= lineGap + border;
\r
4284 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4286 x /= (squareSize + lineGap);
\r
4298 DropEnable dropEnables[] = {
\r
4299 { 'P', DP_Pawn, N_("Pawn") },
\r
4300 { 'N', DP_Knight, N_("Knight") },
\r
4301 { 'B', DP_Bishop, N_("Bishop") },
\r
4302 { 'R', DP_Rook, N_("Rook") },
\r
4303 { 'Q', DP_Queen, N_("Queen") },
\r
4307 SetupDropMenu(HMENU hmenu)
\r
4309 int i, count, enable;
\r
4311 extern char white_holding[], black_holding[];
\r
4312 char item[MSG_SIZ];
\r
4314 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4315 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4316 dropEnables[i].piece);
\r
4318 while (p && *p++ == dropEnables[i].piece) count++;
\r
4319 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4320 enable = count > 0 || !appData.testLegality
\r
4321 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4322 && !appData.icsActive);
\r
4323 ModifyMenu(hmenu, dropEnables[i].command,
\r
4324 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4325 dropEnables[i].command, item);
\r
4329 void DragPieceBegin(int x, int y, Boolean instantly)
\r
4331 dragInfo.lastpos.x = boardRect.left + x;
\r
4332 dragInfo.lastpos.y = boardRect.top + y;
\r
4333 if(instantly) dragInfo.pos = dragInfo.lastpos;
\r
4334 dragInfo.from.x = fromX;
\r
4335 dragInfo.from.y = fromY;
\r
4336 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4337 dragInfo.start = dragInfo.from;
\r
4338 SetCapture(hwndMain);
\r
4341 void DragPieceEnd(int x, int y)
\r
4344 dragInfo.start.x = dragInfo.start.y = -1;
\r
4345 dragInfo.from = dragInfo.start;
\r
4346 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4349 void ChangeDragPiece(ChessSquare piece)
\r
4351 dragInfo.piece = piece;
\r
4354 /* Event handler for mouse messages */
\r
4356 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4360 static int recursive = 0;
\r
4362 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4365 if (message == WM_MBUTTONUP) {
\r
4366 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4367 to the middle button: we simulate pressing the left button too!
\r
4369 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4370 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4376 pt.x = LOWORD(lParam);
\r
4377 pt.y = HIWORD(lParam);
\r
4378 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4379 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4380 if (!flipView && y >= 0) {
\r
4381 y = BOARD_HEIGHT - 1 - y;
\r
4383 if (flipView && x >= 0) {
\r
4384 x = BOARD_WIDTH - 1 - x;
\r
4387 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4388 controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status
\r
4390 switch (message) {
\r
4391 case WM_LBUTTONDOWN:
\r
4392 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4393 ClockClick(flipClock); break;
\r
4394 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4395 ClockClick(!flipClock); break;
\r
4397 if(dragging) { // [HGM] lion: don't destroy dragging info if we are already dragging
\r
4398 dragInfo.start.x = dragInfo.start.y = -1;
\r
4399 dragInfo.from = dragInfo.start;
\r
4401 if(fromX == -1 && frozen) { // not sure where this is for
\r
4402 fromX = fromY = -1;
\r
4403 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4406 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4407 DrawPosition(TRUE, NULL);
\r
4410 case WM_LBUTTONUP:
\r
4411 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4412 DrawPosition(TRUE, NULL);
\r
4415 case WM_MOUSEMOVE:
\r
4416 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4417 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4418 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4419 if ((appData.animateDragging || appData.highlightDragging)
\r
4420 && (wParam & MK_LBUTTON || dragging == 2)
\r
4421 && dragInfo.from.x >= 0)
\r
4423 BOOL full_repaint = FALSE;
\r
4425 if (appData.animateDragging) {
\r
4426 dragInfo.pos = pt;
\r
4428 if (appData.highlightDragging) {
\r
4429 HoverEvent(highlightInfo.sq[1].x, highlightInfo.sq[1].y, x, y);
\r
4430 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4431 full_repaint = TRUE;
\r
4435 DrawPosition( full_repaint, NULL);
\r
4437 dragInfo.lastpos = dragInfo.pos;
\r
4441 case WM_MOUSEWHEEL: // [DM]
\r
4442 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4443 /* Mouse Wheel is being rolled forward
\r
4444 * Play moves forward
\r
4446 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4447 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4448 /* Mouse Wheel is being rolled backward
\r
4449 * Play moves backward
\r
4451 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4452 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4456 case WM_MBUTTONUP:
\r
4457 case WM_RBUTTONUP:
\r
4459 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4462 case WM_MBUTTONDOWN:
\r
4463 case WM_RBUTTONDOWN:
\r
4466 fromX = fromY = -1;
\r
4467 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4468 dragInfo.start.x = dragInfo.start.y = -1;
\r
4469 dragInfo.from = dragInfo.start;
\r
4470 dragInfo.lastpos = dragInfo.pos;
\r
4471 if (appData.highlightDragging) {
\r
4472 ClearHighlights();
\r
4475 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4476 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4477 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4478 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4479 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4483 DrawPosition(TRUE, NULL);
\r
4485 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4488 if (message == WM_MBUTTONDOWN) {
\r
4489 buttonCount = 3; /* even if system didn't think so */
\r
4490 if (wParam & MK_SHIFT)
\r
4491 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4493 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4494 } else { /* message == WM_RBUTTONDOWN */
\r
4495 /* Just have one menu, on the right button. Windows users don't
\r
4496 think to try the middle one, and sometimes other software steals
\r
4497 it, or it doesn't really exist. */
\r
4498 if(gameInfo.variant != VariantShogi)
\r
4499 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4501 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4505 SetCapture(hwndMain);
\r
4508 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4509 SetupDropMenu(hmenu);
\r
4510 MenuPopup(hwnd, pt, hmenu, -1);
\r
4520 /* Preprocess messages for buttons in main window */
\r
4522 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4524 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4527 for (i=0; i<N_BUTTONS; i++) {
\r
4528 if (buttonDesc[i].id == id) break;
\r
4530 if (i == N_BUTTONS) return 0;
\r
4531 switch (message) {
\r
4536 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4537 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4544 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4547 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4548 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4549 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4550 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4552 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4554 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4555 TypeInEvent((char)wParam);
\r
4561 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4564 static int promoStyle;
\r
4566 /* Process messages for Promotion dialog box */
\r
4568 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4573 switch (message) {
\r
4575 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4576 /* Center the dialog over the application window */
\r
4577 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4578 Translate(hDlg, DLG_PromotionKing);
\r
4579 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4580 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4581 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4582 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4583 SW_SHOW : SW_HIDE);
\r
4584 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4585 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4586 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4587 PieceToChar(WhiteAngel) != '~') ||
\r
4588 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4589 PieceToChar(BlackAngel) != '~') ) ?
\r
4590 SW_SHOW : SW_HIDE);
\r
4591 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4592 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4593 PieceToChar(WhiteMarshall) != '~') ||
\r
4594 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4595 PieceToChar(BlackMarshall) != '~') ) ?
\r
4596 SW_SHOW : SW_HIDE);
\r
4597 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4598 ShowWindow(GetDlgItem(hDlg, PB_Rook), !promoStyle ? SW_SHOW : SW_HIDE);
\r
4599 ShowWindow(GetDlgItem(hDlg, PB_Bishop), !promoStyle ? SW_SHOW : SW_HIDE);
\r
4601 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4602 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4603 SetWindowText(hDlg, "Promote?");
\r
4605 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4606 gameInfo.variant == VariantSuper ?
\r
4607 SW_SHOW : SW_HIDE);
\r
4610 case WM_COMMAND: /* message: received a command */
\r
4611 switch (LOWORD(wParam)) {
\r
4613 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4614 ClearHighlights();
\r
4615 DrawPosition(FALSE, NULL);
\r
4618 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4621 promoChar = promoStyle ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4624 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4625 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4628 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4629 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4631 case PB_Chancellor:
\r
4632 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4634 case PB_Archbishop:
\r
4635 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4638 promoChar = gameInfo.variant == VariantShogi ? '=' : promoStyle ? NULLCHAR :
\r
4639 ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight));
\r
4644 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4645 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4646 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4647 fromX = fromY = -1;
\r
4648 if (!appData.highlightLastMove) {
\r
4649 ClearHighlights();
\r
4650 DrawPosition(FALSE, NULL);
\r
4657 /* Pop up promotion dialog */
\r
4659 PromotionPopup(HWND hwnd)
\r
4663 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4664 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4665 hwnd, (DLGPROC)lpProc);
\r
4666 FreeProcInstance(lpProc);
\r
4670 PromotionPopUp(char choice)
\r
4672 promoStyle = (choice == '+' || IS_SHOGI(gameInfo.variant));
\r
4673 DrawPosition(TRUE, NULL);
\r
4674 PromotionPopup(hwndMain);
\r
4678 LoadGameDialog(HWND hwnd, char* title)
\r
4682 char fileTitle[MSG_SIZ];
\r
4683 f = OpenFileDialog(hwnd, "rb", "",
\r
4684 appData.oldSaveStyle ? "gam" : "pgn",
\r
4686 title, &number, fileTitle, NULL);
\r
4688 cmailMsgLoaded = FALSE;
\r
4689 if (number == 0) {
\r
4690 int error = GameListBuild(f);
\r
4692 DisplayError(_("Cannot build game list"), error);
\r
4693 } else if (!ListEmpty(&gameList) &&
\r
4694 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4695 GameListPopUp(f, fileTitle);
\r
4698 GameListDestroy();
\r
4701 LoadGame(f, number, fileTitle, FALSE);
\r
4705 int get_term_width()
\r
4710 HFONT hfont, hold_font;
\r
4715 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4719 // get the text metrics
\r
4720 hdc = GetDC(hText);
\r
4721 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4722 if (consoleCF.dwEffects & CFE_BOLD)
\r
4723 lf.lfWeight = FW_BOLD;
\r
4724 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4725 lf.lfItalic = TRUE;
\r
4726 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4727 lf.lfStrikeOut = TRUE;
\r
4728 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4729 lf.lfUnderline = TRUE;
\r
4730 hfont = CreateFontIndirect(&lf);
\r
4731 hold_font = SelectObject(hdc, hfont);
\r
4732 GetTextMetrics(hdc, &tm);
\r
4733 SelectObject(hdc, hold_font);
\r
4734 DeleteObject(hfont);
\r
4735 ReleaseDC(hText, hdc);
\r
4737 // get the rectangle
\r
4738 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4740 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4743 void UpdateICSWidth(HWND hText)
\r
4745 LONG old_width, new_width;
\r
4747 new_width = get_term_width(hText, FALSE);
\r
4748 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4749 if (new_width != old_width)
\r
4751 ics_update_width(new_width);
\r
4752 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4757 ChangedConsoleFont()
\r
4760 CHARRANGE tmpsel, sel;
\r
4761 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4762 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4763 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4766 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4767 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4768 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4769 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4770 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4771 * size. This was undocumented in the version of MSVC++ that I had
\r
4772 * when I wrote the code, but is apparently documented now.
\r
4774 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4775 cfmt.bCharSet = f->lf.lfCharSet;
\r
4776 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4777 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4778 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4779 /* Why are the following seemingly needed too? */
\r
4780 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4781 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4782 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4784 tmpsel.cpMax = -1; /*999999?*/
\r
4785 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4786 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4787 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4788 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4790 paraf.cbSize = sizeof(paraf);
\r
4791 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4792 paraf.dxStartIndent = 0;
\r
4793 paraf.dxOffset = WRAP_INDENT;
\r
4794 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4795 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4796 UpdateICSWidth(hText);
\r
4799 /*---------------------------------------------------------------------------*\
\r
4801 * Window Proc for main window
\r
4803 \*---------------------------------------------------------------------------*/
\r
4805 /* Process messages for main window, etc. */
\r
4807 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4814 char fileTitle[MSG_SIZ];
\r
4815 static SnapData sd;
\r
4816 static int peek=0;
\r
4818 switch (message) {
\r
4820 case WM_PAINT: /* message: repaint portion of window */
\r
4824 case WM_ERASEBKGND:
\r
4825 if (IsIconic(hwnd)) {
\r
4826 /* Cheat; change the message */
\r
4827 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4829 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4833 case WM_LBUTTONDOWN:
\r
4834 case WM_MBUTTONDOWN:
\r
4835 case WM_RBUTTONDOWN:
\r
4836 case WM_LBUTTONUP:
\r
4837 case WM_MBUTTONUP:
\r
4838 case WM_RBUTTONUP:
\r
4839 case WM_MOUSEMOVE:
\r
4840 case WM_MOUSEWHEEL:
\r
4841 MouseEvent(hwnd, message, wParam, lParam);
\r
4845 if((char)wParam == '\b') {
\r
4846 ForwardEvent(); peek = 0;
\r
4849 JAWS_KBUP_NAVIGATION
\r
4854 if((char)wParam == '\b') {
\r
4855 if(!peek) BackwardEvent(), peek = 1;
\r
4858 JAWS_KBDOWN_NAVIGATION
\r
4864 JAWS_ALT_INTERCEPT
\r
4866 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4867 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4868 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4869 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4871 SendMessage(h, message, wParam, lParam);
\r
4872 } else if(lParam != KF_REPEAT) {
\r
4873 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4874 TypeInEvent((char)wParam);
\r
4875 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4876 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4881 case WM_PALETTECHANGED:
\r
4882 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4884 HDC hdc = GetDC(hwndMain);
\r
4885 SelectPalette(hdc, hPal, TRUE);
\r
4886 nnew = RealizePalette(hdc);
\r
4888 paletteChanged = TRUE;
\r
4890 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4892 ReleaseDC(hwnd, hdc);
\r
4896 case WM_QUERYNEWPALETTE:
\r
4897 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4899 HDC hdc = GetDC(hwndMain);
\r
4900 paletteChanged = FALSE;
\r
4901 SelectPalette(hdc, hPal, FALSE);
\r
4902 nnew = RealizePalette(hdc);
\r
4904 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4906 ReleaseDC(hwnd, hdc);
\r
4911 case WM_COMMAND: /* message: command from application menu */
\r
4912 wmId = LOWORD(wParam);
\r
4917 SAY("new game enter a move to play against the computer with white");
\r
4920 case IDM_NewGameFRC:
\r
4921 if( NewGameFRC() == 0 ) {
\r
4926 case IDM_NewVariant:
\r
4927 NewVariantPopup(hwnd);
\r
4930 case IDM_LoadGame:
\r
4931 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4934 case IDM_LoadNextGame:
\r
4938 case IDM_LoadPrevGame:
\r
4942 case IDM_ReloadGame:
\r
4946 case IDM_LoadPosition:
\r
4947 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4948 Reset(FALSE, TRUE);
\r
4951 f = OpenFileDialog(hwnd, "rb", "",
\r
4952 appData.oldSaveStyle ? "pos" : "fen",
\r
4954 _("Load Position from File"), &number, fileTitle, NULL);
\r
4956 LoadPosition(f, number, fileTitle);
\r
4960 case IDM_LoadNextPosition:
\r
4961 ReloadPosition(1);
\r
4964 case IDM_LoadPrevPosition:
\r
4965 ReloadPosition(-1);
\r
4968 case IDM_ReloadPosition:
\r
4969 ReloadPosition(0);
\r
4972 case IDM_SaveGame:
\r
4973 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4974 f = OpenFileDialog(hwnd, "a", defName,
\r
4975 appData.oldSaveStyle ? "gam" : "pgn",
\r
4977 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4979 SaveGame(f, 0, "");
\r
4983 case IDM_SavePosition:
\r
4984 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4985 f = OpenFileDialog(hwnd, "a", defName,
\r
4986 appData.oldSaveStyle ? "pos" : "fen",
\r
4988 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4990 SavePosition(f, 0, "");
\r
4994 case IDM_SaveDiagram:
\r
4995 defName = "diagram";
\r
4996 f = OpenFileDialog(hwnd, "wb", defName,
\r
4999 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
5005 case IDM_SaveSelected:
\r
5006 f = OpenFileDialog(hwnd, "a", "",
\r
5009 _("Save Game to File"), NULL, fileTitle, NULL);
\r
5011 SaveSelected(f, 0, "");
\r
5015 case IDM_CreateBook:
\r
5016 CreateBookEvent();
\r
5019 case IDM_CopyGame:
\r
5020 CopyGameToClipboard();
\r
5023 case IDM_PasteGame:
\r
5024 PasteGameFromClipboard();
\r
5027 case IDM_CopyGameListToClipboard:
\r
5028 CopyGameListToClipboard();
\r
5031 /* [AS] Autodetect FEN or PGN data */
\r
5032 case IDM_PasteAny:
\r
5033 PasteGameOrFENFromClipboard();
\r
5036 /* [AS] Move history */
\r
5037 case IDM_ShowMoveHistory:
\r
5038 if( MoveHistoryIsUp() ) {
\r
5039 MoveHistoryPopDown();
\r
5042 MoveHistoryPopUp();
\r
5046 /* [AS] Eval graph */
\r
5047 case IDM_ShowEvalGraph:
\r
5048 if( EvalGraphIsUp() ) {
\r
5049 EvalGraphPopDown();
\r
5053 SetFocus(hwndMain);
\r
5057 /* [AS] Engine output */
\r
5058 case IDM_ShowEngineOutput:
\r
5059 if( EngineOutputIsUp() ) {
\r
5060 EngineOutputPopDown();
\r
5063 EngineOutputPopUp();
\r
5067 /* [AS] User adjudication */
\r
5068 case IDM_UserAdjudication_White:
\r
5069 UserAdjudicationEvent( +1 );
\r
5072 case IDM_UserAdjudication_Black:
\r
5073 UserAdjudicationEvent( -1 );
\r
5076 case IDM_UserAdjudication_Draw:
\r
5077 UserAdjudicationEvent( 0 );
\r
5080 /* [AS] Game list options dialog */
\r
5081 case IDM_GameListOptions:
\r
5082 GameListOptions();
\r
5089 case IDM_CopyPosition:
\r
5090 CopyFENToClipboard();
\r
5093 case IDM_PastePosition:
\r
5094 PasteFENFromClipboard();
\r
5097 case IDM_MailMove:
\r
5101 case IDM_ReloadCMailMsg:
\r
5102 Reset(TRUE, TRUE);
\r
5103 ReloadCmailMsgEvent(FALSE);
\r
5106 case IDM_Minimize:
\r
5107 ShowWindow(hwnd, SW_MINIMIZE);
\r
5114 case IDM_MachineWhite:
\r
5115 MachineWhiteEvent();
\r
5117 * refresh the tags dialog only if it's visible
\r
5119 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
5121 tags = PGNTags(&gameInfo);
\r
5122 TagsPopUp(tags, CmailMsg());
\r
5125 SAY("computer starts playing white");
\r
5128 case IDM_MachineBlack:
\r
5129 MachineBlackEvent();
\r
5131 * refresh the tags dialog only if it's visible
\r
5133 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
5135 tags = PGNTags(&gameInfo);
\r
5136 TagsPopUp(tags, CmailMsg());
\r
5139 SAY("computer starts playing black");
\r
5142 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
5143 if(matchMode) EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_GRAYED);
\r
5144 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
5147 case IDM_TwoMachines:
\r
5148 TwoMachinesEvent();
\r
5151 * refresh the tags dialog only if it's visible
\r
5153 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
5155 tags = PGNTags(&gameInfo);
\r
5156 TagsPopUp(tags, CmailMsg());
\r
5159 SAY("computer starts playing both sides");
\r
5162 case IDM_AnalysisMode:
\r
5163 if(AnalyzeModeEvent()) {
\r
5164 SAY("analyzing current position");
\r
5168 case IDM_AnalyzeFile:
\r
5169 AnalyzeFileEvent();
\r
5172 case IDM_IcsClient:
\r
5176 case IDM_EditGame:
\r
5177 case IDM_EditGame2:
\r
5182 case IDM_EditPosition:
\r
5183 case IDM_EditPosition2:
\r
5184 EditPositionEvent();
\r
5185 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
5188 case IDM_Training:
\r
5192 case IDM_ShowGameList:
\r
5193 ShowGameListProc();
\r
5196 case IDM_EditProgs1:
\r
5197 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
5200 case IDM_LoadProg1:
\r
5201 LoadEnginePopUp(hwndMain, 0);
\r
5204 case IDM_LoadProg2:
\r
5205 LoadEnginePopUp(hwndMain, 1);
\r
5208 case IDM_EditServers:
\r
5209 EditTagsPopUp(icsNames, &icsNames);
\r
5212 case IDM_EditTags:
\r
5217 case IDM_EditBook:
\r
5221 case IDM_EditComment:
\r
5223 if (commentUp && editComment) {
\r
5226 EditCommentEvent();
\r
5247 case IDM_CallFlag:
\r
5267 case IDM_StopObserving:
\r
5268 StopObservingEvent();
\r
5271 case IDM_StopExamining:
\r
5272 StopExaminingEvent();
\r
5276 UploadGameEvent();
\r
5279 case IDM_TypeInMove:
\r
5280 TypeInEvent('\000');
\r
5283 case IDM_TypeInName:
\r
5284 PopUpNameDialog('\000');
\r
5287 case IDM_Backward:
\r
5289 SetFocus(hwndMain);
\r
5296 SetFocus(hwndMain);
\r
5301 SetFocus(hwndMain);
\r
5306 SetFocus(hwndMain);
\r
5309 case OPT_GameListNext: // [HGM] forward these two accelerators to Game List
\r
5310 case OPT_GameListPrev:
\r
5311 if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);
\r
5315 RevertEvent(FALSE);
\r
5318 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5319 RevertEvent(TRUE);
\r
5322 case IDM_TruncateGame:
\r
5323 TruncateGameEvent();
\r
5330 case IDM_RetractMove:
\r
5331 RetractMoveEvent();
\r
5334 case IDM_FlipView:
\r
5335 flipView = !flipView;
\r
5336 DrawPosition(FALSE, NULL);
\r
5339 case IDM_FlipClock:
\r
5340 flipClock = !flipClock;
\r
5341 DisplayBothClocks();
\r
5345 case IDM_MuteSounds:
\r
5346 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5347 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5348 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5351 case IDM_GeneralOptions:
\r
5352 GeneralOptionsPopup(hwnd);
\r
5353 DrawPosition(TRUE, NULL);
\r
5356 case IDM_BoardOptions:
\r
5357 BoardOptionsPopup(hwnd);
\r
5360 case IDM_ThemeOptions:
\r
5361 ThemeOptionsPopup(hwnd);
\r
5364 case IDM_EnginePlayOptions:
\r
5365 EnginePlayOptionsPopup(hwnd);
\r
5368 case IDM_Engine1Options:
\r
5369 EngineOptionsPopup(hwnd, &first);
\r
5372 case IDM_Engine2Options:
\r
5374 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5375 EngineOptionsPopup(hwnd, &second);
\r
5378 case IDM_OptionsUCI:
\r
5379 UciOptionsPopup(hwnd);
\r
5383 TourneyPopup(hwnd);
\r
5386 case IDM_IcsOptions:
\r
5387 IcsOptionsPopup(hwnd);
\r
5391 FontsOptionsPopup(hwnd);
\r
5395 SoundOptionsPopup(hwnd);
\r
5398 case IDM_CommPort:
\r
5399 CommPortOptionsPopup(hwnd);
\r
5402 case IDM_LoadOptions:
\r
5403 LoadOptionsPopup(hwnd);
\r
5406 case IDM_SaveOptions:
\r
5407 SaveOptionsPopup(hwnd);
\r
5410 case IDM_TimeControl:
\r
5411 TimeControlOptionsPopup(hwnd);
\r
5414 case IDM_SaveSettings:
\r
5415 SaveSettings(settingsFileName);
\r
5418 case IDM_SaveSettingsOnExit:
\r
5419 saveSettingsOnExit = !saveSettingsOnExit;
\r
5420 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5421 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5422 MF_CHECKED : MF_UNCHECKED));
\r
5433 case IDM_AboutGame:
\r
5438 appData.debugMode = !appData.debugMode;
\r
5439 if (appData.debugMode) {
\r
5440 char dir[MSG_SIZ];
\r
5441 GetCurrentDirectory(MSG_SIZ, dir);
\r
5442 SetCurrentDirectory(installDir);
\r
5443 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5444 SetCurrentDirectory(dir);
\r
5445 setbuf(debugFP, NULL);
\r
5452 case IDM_HELPCONTENTS:
\r
5453 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5454 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5455 MessageBox (GetFocus(),
\r
5456 _("Unable to activate help"),
\r
5457 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5461 case IDM_HELPSEARCH:
\r
5462 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5463 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5464 MessageBox (GetFocus(),
\r
5465 _("Unable to activate help"),
\r
5466 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5470 case IDM_HELPHELP:
\r
5471 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5472 MessageBox (GetFocus(),
\r
5473 _("Unable to activate help"),
\r
5474 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5479 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5481 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5482 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5483 FreeProcInstance(lpProc);
\r
5486 case IDM_DirectCommand1:
\r
5487 AskQuestionEvent(_("Direct Command"),
\r
5488 _("Send to chess program:"), "", "1");
\r
5490 case IDM_DirectCommand2:
\r
5491 AskQuestionEvent(_("Direct Command"),
\r
5492 _("Send to second chess program:"), "", "2");
\r
5495 case EP_WhitePawn:
\r
5496 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5497 fromX = fromY = -1;
\r
5500 case EP_WhiteKnight:
\r
5501 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5502 fromX = fromY = -1;
\r
5505 case EP_WhiteBishop:
\r
5506 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5507 fromX = fromY = -1;
\r
5510 case EP_WhiteRook:
\r
5511 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5512 fromX = fromY = -1;
\r
5515 case EP_WhiteQueen:
\r
5516 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5517 fromX = fromY = -1;
\r
5520 case EP_WhiteFerz:
\r
5521 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5522 fromX = fromY = -1;
\r
5525 case EP_WhiteWazir:
\r
5526 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5527 fromX = fromY = -1;
\r
5530 case EP_WhiteAlfil:
\r
5531 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5532 fromX = fromY = -1;
\r
5535 case EP_WhiteCannon:
\r
5536 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5537 fromX = fromY = -1;
\r
5540 case EP_WhiteCardinal:
\r
5541 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5542 fromX = fromY = -1;
\r
5545 case EP_WhiteMarshall:
\r
5546 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5547 fromX = fromY = -1;
\r
5550 case EP_WhiteKing:
\r
5551 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5552 fromX = fromY = -1;
\r
5555 case EP_BlackPawn:
\r
5556 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5557 fromX = fromY = -1;
\r
5560 case EP_BlackKnight:
\r
5561 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5562 fromX = fromY = -1;
\r
5565 case EP_BlackBishop:
\r
5566 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5567 fromX = fromY = -1;
\r
5570 case EP_BlackRook:
\r
5571 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5572 fromX = fromY = -1;
\r
5575 case EP_BlackQueen:
\r
5576 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5577 fromX = fromY = -1;
\r
5580 case EP_BlackFerz:
\r
5581 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5582 fromX = fromY = -1;
\r
5585 case EP_BlackWazir:
\r
5586 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5587 fromX = fromY = -1;
\r
5590 case EP_BlackAlfil:
\r
5591 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5592 fromX = fromY = -1;
\r
5595 case EP_BlackCannon:
\r
5596 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5597 fromX = fromY = -1;
\r
5600 case EP_BlackCardinal:
\r
5601 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5602 fromX = fromY = -1;
\r
5605 case EP_BlackMarshall:
\r
5606 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5607 fromX = fromY = -1;
\r
5610 case EP_BlackKing:
\r
5611 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5612 fromX = fromY = -1;
\r
5615 case EP_EmptySquare:
\r
5616 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5617 fromX = fromY = -1;
\r
5620 case EP_ClearBoard:
\r
5621 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5622 fromX = fromY = -1;
\r
5626 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5627 fromX = fromY = -1;
\r
5631 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5632 fromX = fromY = -1;
\r
5636 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5637 fromX = fromY = -1;
\r
5641 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5642 fromX = fromY = -1;
\r
5646 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5647 fromX = fromY = -1;
\r
5651 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5652 fromX = fromY = -1;
\r
5656 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5657 fromX = fromY = -1;
\r
5661 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5662 fromX = fromY = -1;
\r
5666 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5667 fromX = fromY = -1;
\r
5671 barbaric = 0; appData.language = "";
\r
5672 TranslateMenus(0);
\r
5673 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5674 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5675 lastChecked = wmId;
\r
5679 if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)
\r
5680 RecentEngineEvent(wmId - IDM_RecentEngines);
\r
5682 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5683 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5684 TranslateMenus(0);
\r
5685 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5686 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5687 lastChecked = wmId;
\r
5690 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5696 case CLOCK_TIMER_ID:
\r
5697 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5698 clockTimerEvent = 0;
\r
5699 DecrementClocks(); /* call into back end */
\r
5701 case LOAD_GAME_TIMER_ID:
\r
5702 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5703 loadGameTimerEvent = 0;
\r
5704 AutoPlayGameLoop(); /* call into back end */
\r
5706 case ANALYSIS_TIMER_ID:
\r
5707 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5708 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5709 AnalysisPeriodicEvent(0);
\r
5711 KillTimer(hwnd, analysisTimerEvent);
\r
5712 analysisTimerEvent = 0;
\r
5715 case DELAYED_TIMER_ID:
\r
5716 KillTimer(hwnd, delayedTimerEvent);
\r
5717 delayedTimerEvent = 0;
\r
5718 delayedTimerCallback();
\r
5723 case WM_USER_Input:
\r
5724 InputEvent(hwnd, message, wParam, lParam);
\r
5727 /* [AS] Also move "attached" child windows */
\r
5728 case WM_WINDOWPOSCHANGING:
\r
5730 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5731 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5733 if( ((lpwp->flags & SWP_NOMOVE) == 0) /*&& ((lpwp->flags & SWP_NOSIZE) != 0)*/ ) { // [HGM] in Win8 size always accompanies move?
\r
5734 /* Window is moving */
\r
5737 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5738 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5739 rcMain.right = wpMain.x + wpMain.width;
\r
5740 rcMain.top = wpMain.y;
\r
5741 rcMain.bottom = wpMain.y + wpMain.height;
\r
5743 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5744 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5745 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5746 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5747 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5748 wpMain.x = lpwp->x;
\r
5749 wpMain.y = lpwp->y;
\r
5755 /* [AS] Snapping */
\r
5756 case WM_ENTERSIZEMOVE:
\r
5757 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5758 if (hwnd == hwndMain) {
\r
5759 doingSizing = TRUE;
\r
5762 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5766 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5767 if (hwnd == hwndMain) {
\r
5768 lastSizing = wParam;
\r
5773 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5774 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5776 case WM_EXITSIZEMOVE:
\r
5777 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5778 if (hwnd == hwndMain) {
\r
5780 doingSizing = FALSE;
\r
5781 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5782 GetClientRect(hwnd, &client);
\r
5783 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5785 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5787 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5790 case WM_DESTROY: /* message: window being destroyed */
\r
5791 PostQuitMessage(0);
\r
5795 if (hwnd == hwndMain) {
\r
5800 default: /* Passes it on if unprocessed */
\r
5801 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5808 /*---------------------------------------------------------------------------*\
\r
5810 * Misc utility routines
\r
5812 \*---------------------------------------------------------------------------*/
\r
5815 * Decent random number generator, at least not as bad as Windows
\r
5816 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5818 unsigned int randstate;
\r
5823 randstate = randstate * 1664525 + 1013904223;
\r
5824 return (int) randstate & 0x7fffffff;
\r
5828 mysrandom(unsigned int seed)
\r
5835 * returns TRUE if user selects a different color, FALSE otherwise
\r
5839 ChangeColor(HWND hwnd, COLORREF *which)
\r
5841 static BOOL firstTime = TRUE;
\r
5842 static DWORD customColors[16];
\r
5844 COLORREF newcolor;
\r
5849 /* Make initial colors in use available as custom colors */
\r
5850 /* Should we put the compiled-in defaults here instead? */
\r
5852 customColors[i++] = lightSquareColor & 0xffffff;
\r
5853 customColors[i++] = darkSquareColor & 0xffffff;
\r
5854 customColors[i++] = whitePieceColor & 0xffffff;
\r
5855 customColors[i++] = blackPieceColor & 0xffffff;
\r
5856 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5857 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5859 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5860 customColors[i++] = textAttribs[ccl].color;
\r
5862 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5863 firstTime = FALSE;
\r
5866 cc.lStructSize = sizeof(cc);
\r
5867 cc.hwndOwner = hwnd;
\r
5868 cc.hInstance = NULL;
\r
5869 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5870 cc.lpCustColors = (LPDWORD) customColors;
\r
5871 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5873 if (!ChooseColor(&cc)) return FALSE;
\r
5875 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5876 if (newcolor == *which) return FALSE;
\r
5877 *which = newcolor;
\r
5881 InitDrawingColors();
\r
5882 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5887 MyLoadSound(MySound *ms)
\r
5893 if (ms->data && ms->flag) free(ms->data);
\r
5896 switch (ms->name[0]) {
\r
5902 /* System sound from Control Panel. Don't preload here. */
\r
5906 if (ms->name[1] == NULLCHAR) {
\r
5907 /* "!" alone = silence */
\r
5910 /* Builtin wave resource. Error if not found. */
\r
5911 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5912 if (h == NULL) break;
\r
5913 ms->data = (void *)LoadResource(hInst, h);
\r
5914 ms->flag = 0; // not maloced, so cannot be freed!
\r
5915 if (h == NULL) break;
\r
5920 /* .wav file. Error if not found. */
\r
5921 f = fopen(ms->name, "rb");
\r
5922 if (f == NULL) break;
\r
5923 if (fstat(fileno(f), &st) < 0) break;
\r
5924 ms->data = malloc(st.st_size);
\r
5926 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5932 char buf[MSG_SIZ];
\r
5933 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5934 DisplayError(buf, GetLastError());
\r
5940 MyPlaySound(MySound *ms)
\r
5942 BOOLEAN ok = FALSE;
\r
5944 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5945 switch (ms->name[0]) {
\r
5947 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5952 /* System sound from Control Panel (deprecated feature).
\r
5953 "$" alone or an unset sound name gets default beep (still in use). */
\r
5954 if (ms->name[1]) {
\r
5955 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5957 if (!ok) ok = MessageBeep(MB_OK);
\r
5960 /* Builtin wave resource, or "!" alone for silence */
\r
5961 if (ms->name[1]) {
\r
5962 if (ms->data == NULL) return FALSE;
\r
5963 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5969 /* .wav file. Error if not found. */
\r
5970 if (ms->data == NULL) return FALSE;
\r
5971 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5974 /* Don't print an error: this can happen innocently if the sound driver
\r
5975 is busy; for instance, if another instance of WinBoard is playing
\r
5976 a sound at about the same time. */
\r
5982 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5985 OPENFILENAME *ofn;
\r
5986 static UINT *number; /* gross that this is static */
\r
5988 switch (message) {
\r
5989 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5990 /* Center the dialog over the application window */
\r
5991 ofn = (OPENFILENAME *) lParam;
\r
5992 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5993 number = (UINT *) ofn->lCustData;
\r
5994 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5998 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5999 Translate(hDlg, 1536);
\r
6000 return FALSE; /* Allow for further processing */
\r
6003 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
6004 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
6006 return FALSE; /* Allow for further processing */
\r
6012 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
6014 static UINT *number;
\r
6015 OPENFILENAME *ofname;
\r
6018 case WM_INITDIALOG:
\r
6019 Translate(hdlg, DLG_IndexNumber);
\r
6020 ofname = (OPENFILENAME *)lParam;
\r
6021 number = (UINT *)(ofname->lCustData);
\r
6024 ofnot = (OFNOTIFY *)lParam;
\r
6025 if (ofnot->hdr.code == CDN_FILEOK) {
\r
6026 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
6035 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
6036 char *nameFilt, char *dlgTitle, UINT *number,
\r
6037 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
6039 OPENFILENAME openFileName;
\r
6040 char buf1[MSG_SIZ];
\r
6043 if (fileName == NULL) fileName = buf1;
\r
6044 if (defName == NULL) {
\r
6045 safeStrCpy(fileName, "*.", 3 );
\r
6046 strcat(fileName, defExt);
\r
6048 safeStrCpy(fileName, defName, MSG_SIZ );
\r
6050 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
6051 if (number) *number = 0;
\r
6053 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
6054 openFileName.hwndOwner = hwnd;
\r
6055 openFileName.hInstance = (HANDLE) hInst;
\r
6056 openFileName.lpstrFilter = nameFilt;
\r
6057 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
6058 openFileName.nMaxCustFilter = 0L;
\r
6059 openFileName.nFilterIndex = 1L;
\r
6060 openFileName.lpstrFile = fileName;
\r
6061 openFileName.nMaxFile = MSG_SIZ;
\r
6062 openFileName.lpstrFileTitle = fileTitle;
\r
6063 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
6064 openFileName.lpstrInitialDir = NULL;
\r
6065 openFileName.lpstrTitle = dlgTitle;
\r
6066 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
6067 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
6068 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
6069 | (oldDialog ? 0 : OFN_EXPLORER);
\r
6070 openFileName.nFileOffset = 0;
\r
6071 openFileName.nFileExtension = 0;
\r
6072 openFileName.lpstrDefExt = defExt;
\r
6073 openFileName.lCustData = (LONG) number;
\r
6074 openFileName.lpfnHook = oldDialog ?
\r
6075 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
6076 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
6078 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
6079 GetOpenFileName(&openFileName)) {
\r
6080 /* open the file */
\r
6081 f = fopen(openFileName.lpstrFile, write);
\r
6083 MessageBox(hwnd, _("File open failed"), NULL,
\r
6084 MB_OK|MB_ICONEXCLAMATION);
\r
6088 int err = CommDlgExtendedError();
\r
6089 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
6098 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
6100 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
6103 * Get the first pop-up menu in the menu template. This is the
\r
6104 * menu that TrackPopupMenu displays.
\r
6106 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
6107 TranslateOneMenu(10, hmenuTrackPopup);
\r
6109 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
6112 * TrackPopup uses screen coordinates, so convert the
\r
6113 * coordinates of the mouse click to screen coordinates.
\r
6115 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
6117 /* Draw and track the floating pop-up menu. */
\r
6118 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
6119 pt.x, pt.y, 0, hwnd, NULL);
\r
6121 /* Destroy the menu.*/
\r
6122 DestroyMenu(hmenu);
\r
6127 int sizeX, sizeY, newSizeX, newSizeY;
\r
6129 } ResizeEditPlusButtonsClosure;
\r
6132 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
6134 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
6138 if (hChild == cl->hText) return TRUE;
\r
6139 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
6140 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
6141 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
6142 ScreenToClient(cl->hDlg, &pt);
\r
6143 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
6144 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
6148 /* Resize a dialog that has a (rich) edit field filling most of
\r
6149 the top, with a row of buttons below */
\r
6151 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
6154 int newTextHeight, newTextWidth;
\r
6155 ResizeEditPlusButtonsClosure cl;
\r
6157 /*if (IsIconic(hDlg)) return;*/
\r
6158 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
6160 cl.hdwp = BeginDeferWindowPos(8);
\r
6162 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
6163 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6164 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6165 if (newTextHeight < 0) {
\r
6166 newSizeY += -newTextHeight;
\r
6167 newTextHeight = 0;
\r
6169 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
6170 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6176 cl.newSizeX = newSizeX;
\r
6177 cl.newSizeY = newSizeY;
\r
6178 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
6180 EndDeferWindowPos(cl.hdwp);
\r
6183 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
6185 RECT rChild, rParent;
\r
6186 int wChild, hChild, wParent, hParent;
\r
6187 int wScreen, hScreen, xNew, yNew;
\r
6190 /* Get the Height and Width of the child window */
\r
6191 GetWindowRect (hwndChild, &rChild);
\r
6192 wChild = rChild.right - rChild.left;
\r
6193 hChild = rChild.bottom - rChild.top;
\r
6195 /* Get the Height and Width of the parent window */
\r
6196 GetWindowRect (hwndParent, &rParent);
\r
6197 wParent = rParent.right - rParent.left;
\r
6198 hParent = rParent.bottom - rParent.top;
\r
6200 /* Get the display limits */
\r
6201 hdc = GetDC (hwndChild);
\r
6202 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
6203 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
6204 ReleaseDC(hwndChild, hdc);
\r
6206 /* Calculate new X position, then adjust for screen */
\r
6207 xNew = rParent.left + ((wParent - wChild) /2);
\r
6210 } else if ((xNew+wChild) > wScreen) {
\r
6211 xNew = wScreen - wChild;
\r
6214 /* Calculate new Y position, then adjust for screen */
\r
6216 yNew = rParent.top + ((hParent - hChild) /2);
\r
6219 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
6224 } else if ((yNew+hChild) > hScreen) {
\r
6225 yNew = hScreen - hChild;
\r
6228 /* Set it, and return */
\r
6229 return SetWindowPos (hwndChild, NULL,
\r
6230 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
6233 /* Center one window over another */
\r
6234 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
6236 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
6239 /*---------------------------------------------------------------------------*\
\r
6241 * Startup Dialog functions
\r
6243 \*---------------------------------------------------------------------------*/
\r
6245 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
6247 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6249 while (*cd != NULL) {
\r
6250 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
6256 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
6258 char buf1[MAX_ARG_LEN];
\r
6261 if (str[0] == '@') {
\r
6262 FILE* f = fopen(str + 1, "r");
\r
6264 DisplayFatalError(str + 1, errno, 2);
\r
6267 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
6269 buf1[len] = NULLCHAR;
\r
6273 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6276 char buf[MSG_SIZ];
\r
6277 char *end = strchr(str, '\n');
\r
6278 if (end == NULL) return;
\r
6279 memcpy(buf, str, end - str);
\r
6280 buf[end - str] = NULLCHAR;
\r
6281 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6287 SetStartupDialogEnables(HWND hDlg)
\r
6289 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6290 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6291 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6292 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6293 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6294 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6295 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6296 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6297 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6298 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6299 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6300 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6301 IsDlgButtonChecked(hDlg, OPT_View));
\r
6305 QuoteForFilename(char *filename)
\r
6307 int dquote, space;
\r
6308 dquote = strchr(filename, '"') != NULL;
\r
6309 space = strchr(filename, ' ') != NULL;
\r
6310 if (dquote || space) {
\r
6322 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6324 char buf[MSG_SIZ];
\r
6327 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6328 q = QuoteForFilename(nthcp);
\r
6329 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6330 if (*nthdir != NULLCHAR) {
\r
6331 q = QuoteForFilename(nthdir);
\r
6332 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6334 if (*nthcp == NULLCHAR) {
\r
6335 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6336 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6337 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6338 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6343 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6345 char buf[MSG_SIZ];
\r
6349 switch (message) {
\r
6350 case WM_INITDIALOG:
\r
6351 /* Center the dialog */
\r
6352 CenterWindow (hDlg, GetDesktopWindow());
\r
6353 Translate(hDlg, DLG_Startup);
\r
6354 /* Initialize the dialog items */
\r
6355 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6356 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6357 firstChessProgramNames);
\r
6358 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6359 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6360 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6361 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6362 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6363 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6364 if (*appData.icsHelper != NULLCHAR) {
\r
6365 char *q = QuoteForFilename(appData.icsHelper);
\r
6366 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6368 if (*appData.icsHost == NULLCHAR) {
\r
6369 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6370 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6371 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6372 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6373 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6376 if (appData.icsActive) {
\r
6377 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6379 else if (appData.noChessProgram) {
\r
6380 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6383 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6386 SetStartupDialogEnables(hDlg);
\r
6390 switch (LOWORD(wParam)) {
\r
6392 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6393 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6394 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6396 comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox
\r
6397 ParseArgs(StringGet, &p);
\r
6398 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6399 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6401 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6402 ParseArgs(StringGet, &p);
\r
6403 SwapEngines(singleList); // ... and then make it 'second'
\r
6405 appData.noChessProgram = FALSE;
\r
6406 appData.icsActive = FALSE;
\r
6407 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6408 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6409 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6411 ParseArgs(StringGet, &p);
\r
6412 if (appData.zippyPlay) {
\r
6413 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6414 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6416 ParseArgs(StringGet, &p);
\r
6418 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6419 appData.noChessProgram = TRUE;
\r
6420 appData.icsActive = FALSE;
\r
6422 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6423 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6426 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6427 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6429 ParseArgs(StringGet, &p);
\r
6431 EndDialog(hDlg, TRUE);
\r
6438 case IDM_HELPCONTENTS:
\r
6439 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6440 MessageBox (GetFocus(),
\r
6441 _("Unable to activate help"),
\r
6442 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6447 SetStartupDialogEnables(hDlg);
\r
6455 /*---------------------------------------------------------------------------*\
\r
6457 * About box dialog functions
\r
6459 \*---------------------------------------------------------------------------*/
\r
6461 /* Process messages for "About" dialog box */
\r
6463 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6465 switch (message) {
\r
6466 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6467 /* Center the dialog over the application window */
\r
6468 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6469 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6470 Translate(hDlg, ABOUTBOX);
\r
6474 case WM_COMMAND: /* message: received a command */
\r
6475 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6476 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6477 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6485 /*---------------------------------------------------------------------------*\
\r
6487 * Comment Dialog functions
\r
6489 \*---------------------------------------------------------------------------*/
\r
6492 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6494 static HANDLE hwndText = NULL;
\r
6495 int len, newSizeX, newSizeY;
\r
6496 static int sizeX, sizeY;
\r
6501 switch (message) {
\r
6502 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6503 /* Initialize the dialog items */
\r
6504 Translate(hDlg, DLG_EditComment);
\r
6505 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6506 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6507 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6508 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6509 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6510 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6511 SetWindowText(hDlg, commentTitle);
\r
6512 if (editComment) {
\r
6513 SetFocus(hwndText);
\r
6515 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6517 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6518 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6519 MAKELPARAM(FALSE, 0));
\r
6520 /* Size and position the dialog */
\r
6521 if (!commentDialog) {
\r
6522 commentDialog = hDlg;
\r
6523 GetClientRect(hDlg, &rect);
\r
6524 sizeX = rect.right;
\r
6525 sizeY = rect.bottom;
\r
6526 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6527 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6528 WINDOWPLACEMENT wp;
\r
6529 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6530 wp.length = sizeof(WINDOWPLACEMENT);
\r
6532 wp.showCmd = SW_SHOW;
\r
6533 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6534 wp.rcNormalPosition.left = wpComment.x;
\r
6535 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6536 wp.rcNormalPosition.top = wpComment.y;
\r
6537 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6538 SetWindowPlacement(hDlg, &wp);
\r
6540 GetClientRect(hDlg, &rect);
\r
6541 newSizeX = rect.right;
\r
6542 newSizeY = rect.bottom;
\r
6543 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6544 newSizeX, newSizeY);
\r
6549 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6552 case WM_COMMAND: /* message: received a command */
\r
6553 switch (LOWORD(wParam)) {
\r
6555 if (editComment) {
\r
6557 /* Read changed options from the dialog box */
\r
6558 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6559 len = GetWindowTextLength(hwndText);
\r
6560 str = (char *) malloc(len + 1);
\r
6561 GetWindowText(hwndText, str, len + 1);
\r
6570 ReplaceComment(commentIndex, str);
\r
6577 case OPT_CancelComment:
\r
6581 case OPT_ClearComment:
\r
6582 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6585 case OPT_EditComment:
\r
6586 EditCommentEvent();
\r
6594 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6595 if( wParam == OPT_CommentText ) {
\r
6596 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6598 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6599 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6603 pt.x = LOWORD( lpMF->lParam );
\r
6604 pt.y = HIWORD( lpMF->lParam );
\r
6606 if(lpMF->msg == WM_CHAR) {
\r
6608 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6609 index = sel.cpMin;
\r
6611 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6613 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6614 len = GetWindowTextLength(hwndText);
\r
6615 str = (char *) malloc(len + 1);
\r
6616 GetWindowText(hwndText, str, len + 1);
\r
6617 ReplaceComment(commentIndex, str);
\r
6618 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6619 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6622 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6623 lpMF->msg = WM_USER;
\r
6631 newSizeX = LOWORD(lParam);
\r
6632 newSizeY = HIWORD(lParam);
\r
6633 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6638 case WM_GETMINMAXINFO:
\r
6639 /* Prevent resizing window too small */
\r
6640 mmi = (MINMAXINFO *) lParam;
\r
6641 mmi->ptMinTrackSize.x = 100;
\r
6642 mmi->ptMinTrackSize.y = 100;
\r
6649 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6654 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6656 if (str == NULL) str = "";
\r
6657 p = (char *) malloc(2 * strlen(str) + 2);
\r
6660 if (*str == '\n') *q++ = '\r';
\r
6664 if (commentText != NULL) free(commentText);
\r
6666 commentIndex = index;
\r
6667 commentTitle = title;
\r
6669 editComment = edit;
\r
6671 if (commentDialog) {
\r
6672 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6673 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6675 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6676 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6677 hwndMain, (DLGPROC)lpProc);
\r
6678 FreeProcInstance(lpProc);
\r
6684 /*---------------------------------------------------------------------------*\
\r
6686 * Type-in move dialog functions
\r
6688 \*---------------------------------------------------------------------------*/
\r
6691 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6693 char move[MSG_SIZ];
\r
6696 switch (message) {
\r
6697 case WM_INITDIALOG:
\r
6698 move[0] = (char) lParam;
\r
6699 move[1] = NULLCHAR;
\r
6700 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6701 Translate(hDlg, DLG_TypeInMove);
\r
6702 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6703 SetWindowText(hInput, move);
\r
6705 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6709 switch (LOWORD(wParam)) {
\r
6712 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6713 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6714 TypeInDoneEvent(move);
\r
6715 EndDialog(hDlg, TRUE);
\r
6718 EndDialog(hDlg, FALSE);
\r
6729 PopUpMoveDialog(char firstchar)
\r
6733 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6734 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6735 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6736 FreeProcInstance(lpProc);
\r
6739 /*---------------------------------------------------------------------------*\
\r
6741 * Type-in name dialog functions
\r
6743 \*---------------------------------------------------------------------------*/
\r
6746 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6748 char move[MSG_SIZ];
\r
6751 switch (message) {
\r
6752 case WM_INITDIALOG:
\r
6753 move[0] = (char) lParam;
\r
6754 move[1] = NULLCHAR;
\r
6755 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6756 Translate(hDlg, DLG_TypeInName);
\r
6757 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6758 SetWindowText(hInput, move);
\r
6760 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6764 switch (LOWORD(wParam)) {
\r
6766 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6767 appData.userName = strdup(move);
\r
6768 SetUserLogo(); DisplayLogos();
\r
6770 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6771 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6772 DisplayTitle(move);
\r
6776 EndDialog(hDlg, TRUE);
\r
6779 EndDialog(hDlg, FALSE);
\r
6790 PopUpNameDialog(char firstchar)
\r
6794 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6795 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6796 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6797 FreeProcInstance(lpProc);
\r
6800 /*---------------------------------------------------------------------------*\
\r
6804 \*---------------------------------------------------------------------------*/
\r
6806 /* Nonmodal error box */
\r
6807 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6808 WPARAM wParam, LPARAM lParam);
\r
6811 ErrorPopUp(char *title, char *content)
\r
6815 BOOLEAN modal = hwndMain == NULL;
\r
6833 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6834 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6837 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6839 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6840 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6841 hwndMain, (DLGPROC)lpProc);
\r
6842 FreeProcInstance(lpProc);
\r
6849 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6850 if (errorDialog == NULL) return;
\r
6851 DestroyWindow(errorDialog);
\r
6852 errorDialog = NULL;
\r
6853 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6857 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6861 switch (message) {
\r
6862 case WM_INITDIALOG:
\r
6863 GetWindowRect(hDlg, &rChild);
\r
6866 SetWindowPos(hDlg, NULL, rChild.left,
\r
6867 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6868 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6872 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6873 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6874 and it doesn't work when you resize the dialog.
\r
6875 For now, just give it a default position.
\r
6877 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6878 Translate(hDlg, DLG_Error);
\r
6880 errorDialog = hDlg;
\r
6881 SetWindowText(hDlg, errorTitle);
\r
6882 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6886 switch (LOWORD(wParam)) {
\r
6889 if (errorDialog == hDlg) errorDialog = NULL;
\r
6890 DestroyWindow(hDlg);
\r
6902 HWND gothicDialog = NULL;
\r
6905 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6908 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6910 switch (message) {
\r
6911 case WM_INITDIALOG:
\r
6912 GetWindowRect(hDlg, &rChild);
\r
6914 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6918 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6919 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6920 and it doesn't work when you resize the dialog.
\r
6921 For now, just give it a default position.
\r
6923 gothicDialog = hDlg;
\r
6924 SetWindowText(hDlg, errorTitle);
\r
6925 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6929 switch (LOWORD(wParam)) {
\r
6932 if (errorDialog == hDlg) errorDialog = NULL;
\r
6933 DestroyWindow(hDlg);
\r
6945 GothicPopUp(char *title, VariantClass variant)
\r
6948 static char *lastTitle;
\r
6950 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6951 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6953 if(lastTitle != title && gothicDialog != NULL) {
\r
6954 DestroyWindow(gothicDialog);
\r
6955 gothicDialog = NULL;
\r
6957 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6958 title = lastTitle;
\r
6959 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6960 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6961 hwndMain, (DLGPROC)lpProc);
\r
6962 FreeProcInstance(lpProc);
\r
6967 /*---------------------------------------------------------------------------*\
\r
6969 * Ics Interaction console functions
\r
6971 \*---------------------------------------------------------------------------*/
\r
6973 #define HISTORY_SIZE 64
\r
6974 static char *history[HISTORY_SIZE];
\r
6975 int histIn = 0, histP = 0;
\r
6979 SaveInHistory(char *cmd)
\r
6981 if (history[histIn] != NULL) {
\r
6982 free(history[histIn]);
\r
6983 history[histIn] = NULL;
\r
6985 if (*cmd == NULLCHAR) return;
\r
6986 history[histIn] = StrSave(cmd);
\r
6987 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6988 if (history[histIn] != NULL) {
\r
6989 free(history[histIn]);
\r
6991 history[histIn] = NULL;
\r
6997 PrevInHistory(char *cmd)
\r
7000 if (histP == histIn) {
\r
7001 if (history[histIn] != NULL) free(history[histIn]);
\r
7002 history[histIn] = StrSave(cmd);
\r
7004 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
7005 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
7007 return history[histP];
\r
7013 if (histP == histIn) return NULL;
\r
7014 histP = (histP + 1) % HISTORY_SIZE;
\r
7015 return history[histP];
\r
7019 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
7023 hmenu = LoadMenu(hInst, "TextMenu");
\r
7024 h = GetSubMenu(hmenu, 0);
\r
7026 if (strcmp(e->item, "-") == 0) {
\r
7027 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
7028 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
7029 int flags = MF_STRING, j = 0;
\r
7030 if (e->item[0] == '|') {
\r
7031 flags |= MF_MENUBARBREAK;
\r
7034 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
7035 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
7043 WNDPROC consoleTextWindowProc;
\r
7046 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
7048 char buf[MSG_SIZ], name[MSG_SIZ];
\r
7049 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7053 SetWindowText(hInput, command);
\r
7055 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
7057 sel.cpMin = 999999;
\r
7058 sel.cpMax = 999999;
\r
7059 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7064 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7065 if (sel.cpMin == sel.cpMax) {
\r
7066 /* Expand to surrounding word */
\r
7069 tr.chrg.cpMax = sel.cpMin;
\r
7070 tr.chrg.cpMin = --sel.cpMin;
\r
7071 if (sel.cpMin < 0) break;
\r
7072 tr.lpstrText = name;
\r
7073 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
7074 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
7078 tr.chrg.cpMin = sel.cpMax;
\r
7079 tr.chrg.cpMax = ++sel.cpMax;
\r
7080 tr.lpstrText = name;
\r
7081 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
7082 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
7085 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
7086 MessageBeep(MB_ICONEXCLAMATION);
\r
7090 tr.lpstrText = name;
\r
7091 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
7093 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
7094 MessageBeep(MB_ICONEXCLAMATION);
\r
7097 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
7100 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
7101 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
7102 SetWindowText(hInput, buf);
\r
7103 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
7105 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
7106 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
7107 SetWindowText(hInput, buf);
\r
7108 sel.cpMin = 999999;
\r
7109 sel.cpMax = 999999;
\r
7110 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7116 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7121 switch (message) {
\r
7123 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7124 if(wParam=='R') return 0;
\r
7127 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
7130 sel.cpMin = 999999;
\r
7131 sel.cpMax = 999999;
\r
7132 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7133 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
7138 if(wParam != '\022') {
\r
7139 if (wParam == '\t') {
\r
7140 if (GetKeyState(VK_SHIFT) < 0) {
\r
7142 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7143 if (buttonDesc[0].hwnd) {
\r
7144 SetFocus(buttonDesc[0].hwnd);
\r
7146 SetFocus(hwndMain);
\r
7150 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
7153 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7154 JAWS_DELETE( SetFocus(hInput); )
\r
7155 SendMessage(hInput, message, wParam, lParam);
\r
7158 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
7160 case WM_RBUTTONDOWN:
\r
7161 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
7162 /* Move selection here if it was empty */
\r
7164 pt.x = LOWORD(lParam);
\r
7165 pt.y = HIWORD(lParam);
\r
7166 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7167 if (sel.cpMin == sel.cpMax) {
\r
7168 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
7169 sel.cpMax = sel.cpMin;
\r
7170 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7172 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
7173 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
7175 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
7176 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7177 if (sel.cpMin == sel.cpMax) {
\r
7178 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7179 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
7181 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7182 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7184 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
7185 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
7186 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
7187 MenuPopup(hwnd, pt, hmenu, -1);
\r
7191 case WM_RBUTTONUP:
\r
7192 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7193 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7194 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7198 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7200 return SendMessage(hInput, message, wParam, lParam);
\r
7201 case WM_MBUTTONDOWN:
\r
7202 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7204 switch (LOWORD(wParam)) {
\r
7205 case IDM_QuickPaste:
\r
7207 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7208 if (sel.cpMin == sel.cpMax) {
\r
7209 MessageBeep(MB_ICONEXCLAMATION);
\r
7212 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7213 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7214 SendMessage(hInput, WM_PASTE, 0, 0);
\r
7219 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7222 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7225 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7229 int i = LOWORD(wParam) - IDM_CommandX;
\r
7230 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
7231 icsTextMenuEntry[i].command != NULL) {
\r
7232 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
7233 icsTextMenuEntry[i].getname,
\r
7234 icsTextMenuEntry[i].immediate);
\r
7242 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
7245 WNDPROC consoleInputWindowProc;
\r
7248 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7250 char buf[MSG_SIZ];
\r
7252 static BOOL sendNextChar = FALSE;
\r
7253 static BOOL quoteNextChar = FALSE;
\r
7254 InputSource *is = consoleInputSource;
\r
7258 switch (message) {
\r
7260 if (!appData.localLineEditing || sendNextChar) {
\r
7261 is->buf[0] = (CHAR) wParam;
\r
7263 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7264 sendNextChar = FALSE;
\r
7267 if (quoteNextChar) {
\r
7268 buf[0] = (char) wParam;
\r
7269 buf[1] = NULLCHAR;
\r
7270 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7271 quoteNextChar = FALSE;
\r
7275 case '\r': /* Enter key */
\r
7276 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7277 if (consoleEcho) SaveInHistory(is->buf);
\r
7278 is->buf[is->count++] = '\n';
\r
7279 is->buf[is->count] = NULLCHAR;
\r
7280 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7281 if (consoleEcho) {
\r
7282 ConsoleOutput(is->buf, is->count, TRUE);
\r
7283 } else if (appData.localLineEditing) {
\r
7284 ConsoleOutput("\n", 1, TRUE);
\r
7287 case '\033': /* Escape key */
\r
7288 SetWindowText(hwnd, "");
\r
7289 cf.cbSize = sizeof(CHARFORMAT);
\r
7290 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7291 if (consoleEcho) {
\r
7292 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7294 cf.crTextColor = COLOR_ECHOOFF;
\r
7296 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7297 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7299 case '\t': /* Tab key */
\r
7300 if (GetKeyState(VK_SHIFT) < 0) {
\r
7302 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7305 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7306 if (buttonDesc[0].hwnd) {
\r
7307 SetFocus(buttonDesc[0].hwnd);
\r
7309 SetFocus(hwndMain);
\r
7313 case '\023': /* Ctrl+S */
\r
7314 sendNextChar = TRUE;
\r
7316 case '\021': /* Ctrl+Q */
\r
7317 quoteNextChar = TRUE;
\r
7327 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7328 p = PrevInHistory(buf);
\r
7330 SetWindowText(hwnd, p);
\r
7331 sel.cpMin = 999999;
\r
7332 sel.cpMax = 999999;
\r
7333 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7338 p = NextInHistory();
\r
7340 SetWindowText(hwnd, p);
\r
7341 sel.cpMin = 999999;
\r
7342 sel.cpMax = 999999;
\r
7343 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7349 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7353 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7357 case WM_MBUTTONDOWN:
\r
7358 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7359 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7361 case WM_RBUTTONUP:
\r
7362 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7363 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7364 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7368 hmenu = LoadMenu(hInst, "InputMenu");
\r
7369 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7370 if (sel.cpMin == sel.cpMax) {
\r
7371 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7372 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7374 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7375 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7377 pt.x = LOWORD(lParam);
\r
7378 pt.y = HIWORD(lParam);
\r
7379 MenuPopup(hwnd, pt, hmenu, -1);
\r
7383 switch (LOWORD(wParam)) {
\r
7385 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7387 case IDM_SelectAll:
\r
7389 sel.cpMax = -1; /*999999?*/
\r
7390 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7393 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7396 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7399 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7404 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7407 #define CO_MAX 100000
\r
7408 #define CO_TRIM 1000
\r
7411 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7413 static SnapData sd;
\r
7414 HWND hText, hInput;
\r
7416 static int sizeX, sizeY;
\r
7417 int newSizeX, newSizeY;
\r
7421 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7422 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7424 switch (message) {
\r
7426 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7428 ENLINK *pLink = (ENLINK*)lParam;
\r
7429 if (pLink->msg == WM_LBUTTONUP)
\r
7433 tr.chrg = pLink->chrg;
\r
7434 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7435 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7436 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7437 free(tr.lpstrText);
\r
7441 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7442 hwndConsole = hDlg;
\r
7444 consoleTextWindowProc = (WNDPROC)
\r
7445 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7446 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7447 consoleInputWindowProc = (WNDPROC)
\r
7448 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7449 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7450 Colorize(ColorNormal, TRUE);
\r
7451 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7452 ChangedConsoleFont();
\r
7453 GetClientRect(hDlg, &rect);
\r
7454 sizeX = rect.right;
\r
7455 sizeY = rect.bottom;
\r
7456 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7457 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7458 WINDOWPLACEMENT wp;
\r
7459 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7460 wp.length = sizeof(WINDOWPLACEMENT);
\r
7462 wp.showCmd = SW_SHOW;
\r
7463 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7464 wp.rcNormalPosition.left = wpConsole.x;
\r
7465 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7466 wp.rcNormalPosition.top = wpConsole.y;
\r
7467 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7468 SetWindowPlacement(hDlg, &wp);
\r
7471 // [HGM] Chessknight's change 2004-07-13
\r
7472 else { /* Determine Defaults */
\r
7473 WINDOWPLACEMENT wp;
\r
7474 wpConsole.x = wpMain.width + 1;
\r
7475 wpConsole.y = wpMain.y;
\r
7476 wpConsole.width = screenWidth - wpMain.width;
\r
7477 wpConsole.height = wpMain.height;
\r
7478 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7479 wp.length = sizeof(WINDOWPLACEMENT);
\r
7481 wp.showCmd = SW_SHOW;
\r
7482 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7483 wp.rcNormalPosition.left = wpConsole.x;
\r
7484 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7485 wp.rcNormalPosition.top = wpConsole.y;
\r
7486 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7487 SetWindowPlacement(hDlg, &wp);
\r
7490 // Allow hText to highlight URLs and send notifications on them
\r
7491 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7492 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7493 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7494 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7508 if (IsIconic(hDlg)) break;
\r
7509 newSizeX = LOWORD(lParam);
\r
7510 newSizeY = HIWORD(lParam);
\r
7511 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7512 RECT rectText, rectInput;
\r
7514 int newTextHeight, newTextWidth;
\r
7515 GetWindowRect(hText, &rectText);
\r
7516 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7517 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7518 if (newTextHeight < 0) {
\r
7519 newSizeY += -newTextHeight;
\r
7520 newTextHeight = 0;
\r
7522 SetWindowPos(hText, NULL, 0, 0,
\r
7523 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7524 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7525 pt.x = rectInput.left;
\r
7526 pt.y = rectInput.top + newSizeY - sizeY;
\r
7527 ScreenToClient(hDlg, &pt);
\r
7528 SetWindowPos(hInput, NULL,
\r
7529 pt.x, pt.y, /* needs client coords */
\r
7530 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7531 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7537 case WM_GETMINMAXINFO:
\r
7538 /* Prevent resizing window too small */
\r
7539 mmi = (MINMAXINFO *) lParam;
\r
7540 mmi->ptMinTrackSize.x = 100;
\r
7541 mmi->ptMinTrackSize.y = 100;
\r
7544 /* [AS] Snapping */
\r
7545 case WM_ENTERSIZEMOVE:
\r
7546 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7549 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7552 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7554 case WM_EXITSIZEMOVE:
\r
7555 UpdateICSWidth(hText);
\r
7556 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7559 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7567 if (hwndConsole) return;
\r
7568 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7569 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7574 ConsoleOutput(char* data, int length, int forceVisible)
\r
7579 char buf[CO_MAX+1];
\r
7582 static int delayLF = 0;
\r
7583 CHARRANGE savesel, sel;
\r
7585 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7593 while (length--) {
\r
7601 } else if (*p == '\007') {
\r
7602 MyPlaySound(&sounds[(int)SoundBell]);
\r
7609 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7610 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7611 /* Save current selection */
\r
7612 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7613 exlen = GetWindowTextLength(hText);
\r
7614 /* Find out whether current end of text is visible */
\r
7615 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7616 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7617 /* Trim existing text if it's too long */
\r
7618 if (exlen + (q - buf) > CO_MAX) {
\r
7619 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7622 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7623 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7625 savesel.cpMin -= trim;
\r
7626 savesel.cpMax -= trim;
\r
7627 if (exlen < 0) exlen = 0;
\r
7628 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7629 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7631 /* Append the new text */
\r
7632 sel.cpMin = exlen;
\r
7633 sel.cpMax = exlen;
\r
7634 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7635 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7636 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7637 if (forceVisible || exlen == 0 ||
\r
7638 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7639 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7640 /* Scroll to make new end of text visible if old end of text
\r
7641 was visible or new text is an echo of user typein */
\r
7642 sel.cpMin = 9999999;
\r
7643 sel.cpMax = 9999999;
\r
7644 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7645 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7646 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7647 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7649 if (savesel.cpMax == exlen || forceVisible) {
\r
7650 /* Move insert point to new end of text if it was at the old
\r
7651 end of text or if the new text is an echo of user typein */
\r
7652 sel.cpMin = 9999999;
\r
7653 sel.cpMax = 9999999;
\r
7654 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7656 /* Restore previous selection */
\r
7657 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7659 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7666 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7670 COLORREF oldFg, oldBg;
\r
7674 if(copyNumber > 1)
\r
7675 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7677 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7678 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7679 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7682 rect.right = x + squareSize;
\r
7684 rect.bottom = y + squareSize;
\r
7687 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7688 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7689 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7690 &rect, str, strlen(str), NULL);
\r
7692 (void) SetTextColor(hdc, oldFg);
\r
7693 (void) SetBkColor(hdc, oldBg);
\r
7694 (void) SelectObject(hdc, oldFont);
\r
7698 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7699 RECT *rect, char *color, char *flagFell)
\r
7703 COLORREF oldFg, oldBg;
\r
7706 if (twoBoards && partnerUp) return;
\r
7707 if (appData.clockMode) {
\r
7708 if (tinyLayout == 2)
\r
7709 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7711 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7718 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7719 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7721 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7722 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7725 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7729 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7730 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7731 rect, str, strlen(str), NULL);
\r
7732 if(logoHeight > 0 && appData.clockMode) {
\r
7734 str += strlen(color)+2;
\r
7735 r.top = rect->top + logoHeight/2;
\r
7736 r.left = rect->left;
\r
7737 r.right = rect->right;
\r
7738 r.bottom = rect->bottom;
\r
7739 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7740 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7741 &r, str, strlen(str), NULL);
\r
7743 (void) SetTextColor(hdc, oldFg);
\r
7744 (void) SetBkColor(hdc, oldBg);
\r
7745 (void) SelectObject(hdc, oldFont);
\r
7750 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7756 if( count <= 0 ) {
\r
7757 if (appData.debugMode) {
\r
7758 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7761 return ERROR_INVALID_USER_BUFFER;
\r
7764 ResetEvent(ovl->hEvent);
\r
7765 ovl->Offset = ovl->OffsetHigh = 0;
\r
7766 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7770 err = GetLastError();
\r
7771 if (err == ERROR_IO_PENDING) {
\r
7772 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7776 err = GetLastError();
\r
7783 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7788 ResetEvent(ovl->hEvent);
\r
7789 ovl->Offset = ovl->OffsetHigh = 0;
\r
7790 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7794 err = GetLastError();
\r
7795 if (err == ERROR_IO_PENDING) {
\r
7796 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7800 err = GetLastError();
\r
7807 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7808 void CheckForInputBufferFull( InputSource * is )
\r
7810 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7811 /* Look for end of line */
\r
7812 char * p = is->buf;
\r
7814 while( p < is->next && *p != '\n' ) {
\r
7818 if( p >= is->next ) {
\r
7819 if (appData.debugMode) {
\r
7820 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7823 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7824 is->count = (DWORD) -1;
\r
7825 is->next = is->buf;
\r
7831 InputThread(LPVOID arg)
\r
7836 is = (InputSource *) arg;
\r
7837 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7838 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7839 while (is->hThread != NULL) {
\r
7840 is->error = DoReadFile(is->hFile, is->next,
\r
7841 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7842 &is->count, &ovl);
\r
7843 if (is->error == NO_ERROR) {
\r
7844 is->next += is->count;
\r
7846 if (is->error == ERROR_BROKEN_PIPE) {
\r
7847 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7850 is->count = (DWORD) -1;
\r
7851 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7856 CheckForInputBufferFull( is );
\r
7858 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7860 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7862 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7865 CloseHandle(ovl.hEvent);
\r
7866 CloseHandle(is->hFile);
\r
7868 if (appData.debugMode) {
\r
7869 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7876 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7878 NonOvlInputThread(LPVOID arg)
\r
7885 is = (InputSource *) arg;
\r
7886 while (is->hThread != NULL) {
\r
7887 is->error = ReadFile(is->hFile, is->next,
\r
7888 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7889 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7890 if (is->error == NO_ERROR) {
\r
7891 /* Change CRLF to LF */
\r
7892 if (is->next > is->buf) {
\r
7894 i = is->count + 1;
\r
7902 if (prev == '\r' && *p == '\n') {
\r
7914 if (is->error == ERROR_BROKEN_PIPE) {
\r
7915 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7918 is->count = (DWORD) -1;
\r
7922 CheckForInputBufferFull( is );
\r
7924 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7926 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7928 if (is->count < 0) break; /* Quit on error */
\r
7930 CloseHandle(is->hFile);
\r
7935 SocketInputThread(LPVOID arg)
\r
7939 is = (InputSource *) arg;
\r
7940 while (is->hThread != NULL) {
\r
7941 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7942 if ((int)is->count == SOCKET_ERROR) {
\r
7943 is->count = (DWORD) -1;
\r
7944 is->error = WSAGetLastError();
\r
7946 is->error = NO_ERROR;
\r
7947 is->next += is->count;
\r
7948 if (is->count == 0 && is->second == is) {
\r
7949 /* End of file on stderr; quit with no message */
\r
7953 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7955 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7957 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7963 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7967 is = (InputSource *) lParam;
\r
7968 if (is->lineByLine) {
\r
7969 /* Feed in lines one by one */
\r
7970 char *p = is->buf;
\r
7972 while (q < is->next) {
\r
7973 if (*q++ == '\n') {
\r
7974 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7979 /* Move any partial line to the start of the buffer */
\r
7981 while (p < is->next) {
\r
7986 if (is->error != NO_ERROR || is->count == 0) {
\r
7987 /* Notify backend of the error. Note: If there was a partial
\r
7988 line at the end, it is not flushed through. */
\r
7989 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7992 /* Feed in the whole chunk of input at once */
\r
7993 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7994 is->next = is->buf;
\r
7998 /*---------------------------------------------------------------------------*\
\r
8000 * Menu enables. Used when setting various modes.
\r
8002 \*---------------------------------------------------------------------------*/
\r
8010 GreyRevert(Boolean grey)
\r
8011 { // [HGM] vari: for retracting variations in local mode
\r
8012 HMENU hmenu = GetMenu(hwndMain);
\r
8013 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
8014 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
8018 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
8020 while (enab->item > 0) {
\r
8021 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
8026 Enables gnuEnables[] = {
\r
8027 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
8028 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
8029 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
8030 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
8031 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
8032 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
8033 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
8034 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
8035 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
8036 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
8037 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
8038 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
8039 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
8042 // Needed to switch from ncp to GNU mode on Engine Load
\r
8043 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
8044 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
8045 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
8046 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
8047 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
8048 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
8049 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
8050 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
8051 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
8052 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
8053 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
8054 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8055 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
8056 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
8060 Enables icsEnables[] = {
\r
8061 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
8062 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
8063 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8064 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8065 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8066 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8067 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
8068 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
8069 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
8070 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
8071 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8072 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
8073 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
8074 { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },
\r
8075 { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },
\r
8076 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
8077 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
8078 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
8079 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
8080 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
8085 Enables zippyEnables[] = {
\r
8086 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8087 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
8088 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
8089 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
8094 Enables ncpEnables[] = {
\r
8095 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
8096 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
8097 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8098 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8099 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8100 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8101 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
8102 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
8103 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
8104 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
8105 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
8106 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
8107 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8108 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
8109 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
8110 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
8111 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
8112 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
8113 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
8114 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
8115 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
8116 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
8120 Enables trainingOnEnables[] = {
\r
8121 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
8122 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
8123 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
8124 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
8125 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
8126 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
8127 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
8128 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8129 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
8133 Enables trainingOffEnables[] = {
\r
8134 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
8135 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
8136 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
8137 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
8138 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
8139 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
8140 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
8141 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8142 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
8146 /* These modify either ncpEnables or gnuEnables */
\r
8147 Enables cmailEnables[] = {
\r
8148 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
8149 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
8150 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
8151 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
8152 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
8153 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
8154 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
8158 Enables machineThinkingEnables[] = {
\r
8159 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8160 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
8161 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
8162 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8163 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
8164 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8165 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8166 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8167 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8168 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
8169 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8170 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8171 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8172 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8173 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
8174 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
8178 Enables userThinkingEnables[] = {
\r
8179 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8180 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
8181 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
8182 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8183 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
8184 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8185 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8186 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8187 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8188 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
8189 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
8190 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
8191 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
8192 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
8193 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
8194 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
8198 /*---------------------------------------------------------------------------*\
\r
8200 * Front-end interface functions exported by XBoard.
\r
8201 * Functions appear in same order as prototypes in frontend.h.
\r
8203 \*---------------------------------------------------------------------------*/
\r
8205 CheckMark(UINT item, int state)
\r
8207 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
8213 static UINT prevChecked = 0;
\r
8214 static int prevPausing = 0;
\r
8217 if (pausing != prevPausing) {
\r
8218 prevPausing = pausing;
\r
8219 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
8220 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
8221 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
8224 switch (gameMode) {
\r
8225 case BeginningOfGame:
\r
8226 if (appData.icsActive)
\r
8227 nowChecked = IDM_IcsClient;
\r
8228 else if (appData.noChessProgram)
\r
8229 nowChecked = IDM_EditGame;
\r
8231 nowChecked = IDM_MachineBlack;
\r
8233 case MachinePlaysBlack:
\r
8234 nowChecked = IDM_MachineBlack;
\r
8236 case MachinePlaysWhite:
\r
8237 nowChecked = IDM_MachineWhite;
\r
8239 case TwoMachinesPlay:
\r
8240 nowChecked = IDM_TwoMachines;
\r
8243 nowChecked = IDM_AnalysisMode;
\r
8246 nowChecked = IDM_AnalyzeFile;
\r
8249 nowChecked = IDM_EditGame;
\r
8251 case PlayFromGameFile:
\r
8252 nowChecked = IDM_LoadGame;
\r
8254 case EditPosition:
\r
8255 nowChecked = IDM_EditPosition;
\r
8258 nowChecked = IDM_Training;
\r
8260 case IcsPlayingWhite:
\r
8261 case IcsPlayingBlack:
\r
8262 case IcsObserving:
\r
8264 nowChecked = IDM_IcsClient;
\r
8271 if(prevChecked == IDM_TwoMachines) // [HGM] 'Machine Match' might have gotten disabled when stopping match
\r
8272 EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_ENABLED);
\r
8273 CheckMark(prevChecked, MF_UNCHECKED);
\r
8274 CheckMark(nowChecked, MF_CHECKED);
\r
8275 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8277 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8278 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8279 MF_BYCOMMAND|MF_ENABLED);
\r
8281 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8282 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8285 prevChecked = nowChecked;
\r
8287 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8288 if (appData.icsActive) {
\r
8289 if (appData.icsEngineAnalyze) {
\r
8290 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8292 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8295 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8301 HMENU hmenu = GetMenu(hwndMain);
\r
8302 SetMenuEnables(hmenu, icsEnables);
\r
8303 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8304 MF_BYCOMMAND|MF_ENABLED);
\r
8306 if (appData.zippyPlay) {
\r
8307 SetMenuEnables(hmenu, zippyEnables);
\r
8308 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8309 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8310 MF_BYCOMMAND|MF_ENABLED);
\r
8318 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8324 HMENU hmenu = GetMenu(hwndMain);
\r
8325 SetMenuEnables(hmenu, ncpEnables);
\r
8326 DrawMenuBar(hwndMain);
\r
8332 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8336 SetTrainingModeOn()
\r
8339 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8340 for (i = 0; i < N_BUTTONS; i++) {
\r
8341 if (buttonDesc[i].hwnd != NULL)
\r
8342 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8347 VOID SetTrainingModeOff()
\r
8350 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8351 for (i = 0; i < N_BUTTONS; i++) {
\r
8352 if (buttonDesc[i].hwnd != NULL)
\r
8353 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8359 SetUserThinkingEnables()
\r
8361 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8365 SetMachineThinkingEnables()
\r
8367 HMENU hMenu = GetMenu(hwndMain);
\r
8368 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8370 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8372 if (gameMode == MachinePlaysBlack) {
\r
8373 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8374 } else if (gameMode == MachinePlaysWhite) {
\r
8375 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8376 } else if (gameMode == TwoMachinesPlay) {
\r
8377 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8383 DisplayTitle(char *str)
\r
8385 char title[MSG_SIZ], *host;
\r
8386 if (str[0] != NULLCHAR) {
\r
8387 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8388 } else if (appData.icsActive) {
\r
8389 if (appData.icsCommPort[0] != NULLCHAR)
\r
8392 host = appData.icsHost;
\r
8393 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8394 } else if (appData.noChessProgram) {
\r
8395 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8397 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8398 strcat(title, ": ");
\r
8399 strcat(title, first.tidy);
\r
8401 SetWindowText(hwndMain, title);
\r
8406 DisplayMessage(char *str1, char *str2)
\r
8410 int remain = MESSAGE_TEXT_MAX - 1;
\r
8413 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8414 messageText[0] = NULLCHAR;
\r
8416 len = strlen(str1);
\r
8417 if (len > remain) len = remain;
\r
8418 strncpy(messageText, str1, len);
\r
8419 messageText[len] = NULLCHAR;
\r
8422 if (*str2 && remain >= 2) {
\r
8424 strcat(messageText, " ");
\r
8427 len = strlen(str2);
\r
8428 if (len > remain) len = remain;
\r
8429 strncat(messageText, str2, len);
\r
8431 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8432 safeStrCpy(lastMsg, messageText, MSG_SIZ);
\r
8434 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8438 hdc = GetDC(hwndMain);
\r
8439 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8440 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8441 &messageRect, messageText, strlen(messageText), NULL);
\r
8442 (void) SelectObject(hdc, oldFont);
\r
8443 (void) ReleaseDC(hwndMain, hdc);
\r
8447 DisplayError(char *str, int error)
\r
8449 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8453 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8455 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8456 NULL, error, LANG_NEUTRAL,
\r
8457 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8459 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8461 ErrorMap *em = errmap;
\r
8462 while (em->err != 0 && em->err != error) em++;
\r
8463 if (em->err != 0) {
\r
8464 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8466 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8471 ErrorPopUp(_("Error"), buf);
\r
8476 DisplayMoveError(char *str)
\r
8478 fromX = fromY = -1;
\r
8479 ClearHighlights();
\r
8480 DrawPosition(FALSE, NULL);
\r
8481 if (appData.popupMoveErrors) {
\r
8482 ErrorPopUp(_("Error"), str);
\r
8484 DisplayMessage(str, "");
\r
8485 moveErrorMessageUp = TRUE;
\r
8490 DisplayFatalError(char *str, int error, int exitStatus)
\r
8492 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8494 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8497 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8498 NULL, error, LANG_NEUTRAL,
\r
8499 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8501 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8503 ErrorMap *em = errmap;
\r
8504 while (em->err != 0 && em->err != error) em++;
\r
8505 if (em->err != 0) {
\r
8506 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8508 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8513 if (appData.debugMode) {
\r
8514 fprintf(debugFP, "%s: %s\n", label, str);
\r
8516 if (appData.popupExitMessage) {
\r
8517 if(appData.icsActive) SendToICS("logout\n"); // [HGM] make sure no new games will be started!
\r
8518 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8519 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8521 ExitEvent(exitStatus);
\r
8526 DisplayInformation(char *str)
\r
8528 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8533 DisplayNote(char *str)
\r
8535 ErrorPopUp(_("Note"), str);
\r
8540 char *title, *question, *replyPrefix;
\r
8545 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8547 static QuestionParams *qp;
\r
8548 char reply[MSG_SIZ];
\r
8551 switch (message) {
\r
8552 case WM_INITDIALOG:
\r
8553 qp = (QuestionParams *) lParam;
\r
8554 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8555 Translate(hDlg, DLG_Question);
\r
8556 SetWindowText(hDlg, qp->title);
\r
8557 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8558 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8562 switch (LOWORD(wParam)) {
\r
8564 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8565 if (*reply) strcat(reply, " ");
\r
8566 len = strlen(reply);
\r
8567 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8568 strcat(reply, "\n");
\r
8569 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8570 EndDialog(hDlg, TRUE);
\r
8571 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8574 EndDialog(hDlg, FALSE);
\r
8585 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8587 QuestionParams qp;
\r
8591 qp.question = question;
\r
8592 qp.replyPrefix = replyPrefix;
\r
8594 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8595 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8596 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8597 FreeProcInstance(lpProc);
\r
8600 /* [AS] Pick FRC position */
\r
8601 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8603 static int * lpIndexFRC;
\r
8609 case WM_INITDIALOG:
\r
8610 lpIndexFRC = (int *) lParam;
\r
8612 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8613 Translate(hDlg, DLG_NewGameFRC);
\r
8615 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8616 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8617 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8618 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8623 switch( LOWORD(wParam) ) {
\r
8625 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8626 EndDialog( hDlg, 0 );
\r
8627 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8630 EndDialog( hDlg, 1 );
\r
8632 case IDC_NFG_Edit:
\r
8633 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8634 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8636 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8639 case IDC_NFG_Random:
\r
8640 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8641 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8654 int index = appData.defaultFrcPosition;
\r
8655 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8657 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8659 if( result == 0 ) {
\r
8660 appData.defaultFrcPosition = index;
\r
8666 /* [AS] Game list options. Refactored by HGM */
\r
8668 HWND gameListOptionsDialog;
\r
8670 // low-level front-end: clear text edit / list widget
\r
8675 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8678 // low-level front-end: clear text edit / list widget
\r
8680 GLT_DeSelectList()
\r
8682 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8685 // low-level front-end: append line to text edit / list widget
\r
8687 GLT_AddToList( char *name )
\r
8690 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8694 // low-level front-end: get line from text edit / list widget
\r
8696 GLT_GetFromList( int index, char *name )
\r
8699 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8705 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8707 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8708 int idx2 = idx1 + delta;
\r
8709 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8711 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8714 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8715 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8716 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8717 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8721 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8725 case WM_INITDIALOG:
\r
8726 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8728 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8729 Translate(hDlg, DLG_GameListOptions);
\r
8731 /* Initialize list */
\r
8732 GLT_TagsToList( lpUserGLT );
\r
8734 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8739 switch( LOWORD(wParam) ) {
\r
8742 EndDialog( hDlg, 0 );
\r
8745 EndDialog( hDlg, 1 );
\r
8748 case IDC_GLT_Default:
\r
8749 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8752 case IDC_GLT_Restore:
\r
8753 GLT_TagsToList( appData.gameListTags );
\r
8757 GLT_MoveSelection( hDlg, -1 );
\r
8760 case IDC_GLT_Down:
\r
8761 GLT_MoveSelection( hDlg, +1 );
\r
8771 int GameListOptions()
\r
8774 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8776 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8778 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8780 if( result == 0 ) {
\r
8781 char *oldTags = appData.gameListTags;
\r
8782 /* [AS] Memory leak here! */
\r
8783 appData.gameListTags = strdup( lpUserGLT );
\r
8784 if(strcmp(oldTags, appData.gameListTags)) // [HGM] redo Game List when we changed something
\r
8785 GameListToListBox(NULL, TRUE, ".", NULL, FALSE, FALSE); // "." as filter is kludge to select all
\r
8792 DisplayIcsInteractionTitle(char *str)
\r
8794 char consoleTitle[MSG_SIZ];
\r
8796 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8797 SetWindowText(hwndConsole, consoleTitle);
\r
8799 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
8800 char buf[MSG_SIZ], *p = buf, *q;
\r
8801 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
8803 q = strchr(p, ';');
\r
8805 if(*p) ChatPopUp(p);
\r
8809 SetActiveWindow(hwndMain);
\r
8813 DrawPosition(int fullRedraw, Board board)
\r
8815 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8818 void NotifyFrontendLogin()
\r
8821 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8827 fromX = fromY = -1;
\r
8828 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8829 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8830 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8831 dragInfo.lastpos = dragInfo.pos;
\r
8832 dragInfo.start.x = dragInfo.start.y = -1;
\r
8833 dragInfo.from = dragInfo.start;
\r
8835 DrawPosition(TRUE, NULL);
\r
8842 CommentPopUp(char *title, char *str)
\r
8844 HWND hwnd = GetActiveWindow();
\r
8845 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8847 SetActiveWindow(hwnd);
\r
8851 CommentPopDown(void)
\r
8853 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8854 if (commentDialog) {
\r
8855 ShowWindow(commentDialog, SW_HIDE);
\r
8857 commentUp = FALSE;
\r
8861 EditCommentPopUp(int index, char *title, char *str)
\r
8863 EitherCommentPopUp(index, title, str, TRUE);
\r
8870 MyPlaySound(&sounds[(int)SoundRoar]);
\r
8877 MyPlaySound(&sounds[(int)SoundMove]);
\r
8880 VOID PlayIcsWinSound()
\r
8882 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8885 VOID PlayIcsLossSound()
\r
8887 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8890 VOID PlayIcsDrawSound()
\r
8892 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8895 VOID PlayIcsUnfinishedSound()
\r
8897 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8903 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8909 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8917 consoleEcho = TRUE;
\r
8918 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8919 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8920 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8929 consoleEcho = FALSE;
\r
8930 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8931 /* This works OK: set text and background both to the same color */
\r
8933 cf.crTextColor = COLOR_ECHOOFF;
\r
8934 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8935 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8938 /* No Raw()...? */
\r
8940 void Colorize(ColorClass cc, int continuation)
\r
8942 currentColorClass = cc;
\r
8943 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8944 consoleCF.crTextColor = textAttribs[cc].color;
\r
8945 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8946 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8952 static char buf[MSG_SIZ];
\r
8953 DWORD bufsiz = MSG_SIZ;
\r
8955 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8956 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8958 if (!GetUserName(buf, &bufsiz)) {
\r
8959 /*DisplayError("Error getting user name", GetLastError());*/
\r
8960 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8968 static char buf[MSG_SIZ];
\r
8969 DWORD bufsiz = MSG_SIZ;
\r
8971 if (!GetComputerName(buf, &bufsiz)) {
\r
8972 /*DisplayError("Error getting host name", GetLastError());*/
\r
8973 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8980 ClockTimerRunning()
\r
8982 return clockTimerEvent != 0;
\r
8988 if (clockTimerEvent == 0) return FALSE;
\r
8989 KillTimer(hwndMain, clockTimerEvent);
\r
8990 clockTimerEvent = 0;
\r
8995 StartClockTimer(long millisec)
\r
8997 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8998 (UINT) millisec, NULL);
\r
9002 DisplayWhiteClock(long timeRemaining, int highlight)
\r
9005 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
9007 if(appData.noGUI) return;
\r
9008 hdc = GetDC(hwndMain);
\r
9009 if (!IsIconic(hwndMain)) {
\r
9010 DisplayAClock(hdc, timeRemaining, highlight,
\r
9011 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
9013 if (highlight && iconCurrent == iconBlack) {
\r
9014 iconCurrent = iconWhite;
\r
9015 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
9016 if (IsIconic(hwndMain)) {
\r
9017 DrawIcon(hdc, 2, 2, iconCurrent);
\r
9020 (void) ReleaseDC(hwndMain, hdc);
\r
9022 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
9026 DisplayBlackClock(long timeRemaining, int highlight)
\r
9029 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
9032 if(appData.noGUI) return;
\r
9033 hdc = GetDC(hwndMain);
\r
9034 if (!IsIconic(hwndMain)) {
\r
9035 DisplayAClock(hdc, timeRemaining, highlight,
\r
9036 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
9038 if (highlight && iconCurrent == iconWhite) {
\r
9039 iconCurrent = iconBlack;
\r
9040 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
9041 if (IsIconic(hwndMain)) {
\r
9042 DrawIcon(hdc, 2, 2, iconCurrent);
\r
9045 (void) ReleaseDC(hwndMain, hdc);
\r
9047 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
9052 LoadGameTimerRunning()
\r
9054 return loadGameTimerEvent != 0;
\r
9058 StopLoadGameTimer()
\r
9060 if (loadGameTimerEvent == 0) return FALSE;
\r
9061 KillTimer(hwndMain, loadGameTimerEvent);
\r
9062 loadGameTimerEvent = 0;
\r
9067 StartLoadGameTimer(long millisec)
\r
9069 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
9070 (UINT) millisec, NULL);
\r
9078 char fileTitle[MSG_SIZ];
\r
9080 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
9081 f = OpenFileDialog(hwndMain, "a", defName,
\r
9082 appData.oldSaveStyle ? "gam" : "pgn",
\r
9084 _("Save Game to File"), NULL, fileTitle, NULL);
\r
9086 SaveGame(f, 0, "");
\r
9093 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
9095 if (delayedTimerEvent != 0) {
\r
9096 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
9097 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
9099 KillTimer(hwndMain, delayedTimerEvent);
\r
9100 delayedTimerEvent = 0;
\r
9101 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
9102 delayedTimerCallback();
\r
9104 delayedTimerCallback = cb;
\r
9105 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
9106 (UINT) millisec, NULL);
\r
9109 DelayedEventCallback
\r
9112 if (delayedTimerEvent) {
\r
9113 return delayedTimerCallback;
\r
9120 CancelDelayedEvent()
\r
9122 if (delayedTimerEvent) {
\r
9123 KillTimer(hwndMain, delayedTimerEvent);
\r
9124 delayedTimerEvent = 0;
\r
9128 DWORD GetWin32Priority(int nice)
\r
9129 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
9131 REALTIME_PRIORITY_CLASS 0x00000100
\r
9132 HIGH_PRIORITY_CLASS 0x00000080
\r
9133 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
9134 NORMAL_PRIORITY_CLASS 0x00000020
\r
9135 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
9136 IDLE_PRIORITY_CLASS 0x00000040
\r
9138 if (nice < -15) return 0x00000080;
\r
9139 if (nice < 0) return 0x00008000;
\r
9140 if (nice == 0) return 0x00000020;
\r
9141 if (nice < 15) return 0x00004000;
\r
9142 return 0x00000040;
\r
9145 void RunCommand(char *cmdLine)
\r
9147 /* Now create the child process. */
\r
9148 STARTUPINFO siStartInfo;
\r
9149 PROCESS_INFORMATION piProcInfo;
\r
9151 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9152 siStartInfo.lpReserved = NULL;
\r
9153 siStartInfo.lpDesktop = NULL;
\r
9154 siStartInfo.lpTitle = NULL;
\r
9155 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9156 siStartInfo.cbReserved2 = 0;
\r
9157 siStartInfo.lpReserved2 = NULL;
\r
9158 siStartInfo.hStdInput = NULL;
\r
9159 siStartInfo.hStdOutput = NULL;
\r
9160 siStartInfo.hStdError = NULL;
\r
9162 CreateProcess(NULL,
\r
9163 cmdLine, /* command line */
\r
9164 NULL, /* process security attributes */
\r
9165 NULL, /* primary thread security attrs */
\r
9166 TRUE, /* handles are inherited */
\r
9167 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9168 NULL, /* use parent's environment */
\r
9170 &siStartInfo, /* STARTUPINFO pointer */
\r
9171 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9173 CloseHandle(piProcInfo.hThread);
\r
9176 /* Start a child process running the given program.
\r
9177 The process's standard output can be read from "from", and its
\r
9178 standard input can be written to "to".
\r
9179 Exit with fatal error if anything goes wrong.
\r
9180 Returns an opaque pointer that can be used to destroy the process
\r
9184 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
9186 #define BUFSIZE 4096
\r
9188 HANDLE hChildStdinRd, hChildStdinWr,
\r
9189 hChildStdoutRd, hChildStdoutWr;
\r
9190 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
9191 SECURITY_ATTRIBUTES saAttr;
\r
9193 PROCESS_INFORMATION piProcInfo;
\r
9194 STARTUPINFO siStartInfo;
\r
9196 char buf[MSG_SIZ];
\r
9199 if (appData.debugMode) {
\r
9200 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
9205 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
9206 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
9207 saAttr.bInheritHandle = TRUE;
\r
9208 saAttr.lpSecurityDescriptor = NULL;
\r
9211 * The steps for redirecting child's STDOUT:
\r
9212 * 1. Create anonymous pipe to be STDOUT for child.
\r
9213 * 2. Create a noninheritable duplicate of read handle,
\r
9214 * and close the inheritable read handle.
\r
9217 /* Create a pipe for the child's STDOUT. */
\r
9218 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
9219 return GetLastError();
\r
9222 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
9223 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
9224 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
9225 FALSE, /* not inherited */
\r
9226 DUPLICATE_SAME_ACCESS);
\r
9228 return GetLastError();
\r
9230 CloseHandle(hChildStdoutRd);
\r
9233 * The steps for redirecting child's STDIN:
\r
9234 * 1. Create anonymous pipe to be STDIN for child.
\r
9235 * 2. Create a noninheritable duplicate of write handle,
\r
9236 * and close the inheritable write handle.
\r
9239 /* Create a pipe for the child's STDIN. */
\r
9240 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
9241 return GetLastError();
\r
9244 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
9245 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
9246 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
9247 FALSE, /* not inherited */
\r
9248 DUPLICATE_SAME_ACCESS);
\r
9250 return GetLastError();
\r
9252 CloseHandle(hChildStdinWr);
\r
9254 /* Arrange to (1) look in dir for the child .exe file, and
\r
9255 * (2) have dir be the child's working directory. Interpret
\r
9256 * dir relative to the directory WinBoard loaded from. */
\r
9257 GetCurrentDirectory(MSG_SIZ, buf);
\r
9258 SetCurrentDirectory(installDir);
\r
9259 SetCurrentDirectory(dir);
\r
9261 /* Now create the child process. */
\r
9263 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9264 siStartInfo.lpReserved = NULL;
\r
9265 siStartInfo.lpDesktop = NULL;
\r
9266 siStartInfo.lpTitle = NULL;
\r
9267 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9268 siStartInfo.cbReserved2 = 0;
\r
9269 siStartInfo.lpReserved2 = NULL;
\r
9270 siStartInfo.hStdInput = hChildStdinRd;
\r
9271 siStartInfo.hStdOutput = hChildStdoutWr;
\r
9272 siStartInfo.hStdError = hChildStdoutWr;
\r
9274 fSuccess = CreateProcess(NULL,
\r
9275 cmdLine, /* command line */
\r
9276 NULL, /* process security attributes */
\r
9277 NULL, /* primary thread security attrs */
\r
9278 TRUE, /* handles are inherited */
\r
9279 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9280 NULL, /* use parent's environment */
\r
9282 &siStartInfo, /* STARTUPINFO pointer */
\r
9283 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9285 err = GetLastError();
\r
9286 SetCurrentDirectory(buf); /* return to prev directory */
\r
9291 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
9292 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
9293 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
9296 /* Close the handles we don't need in the parent */
\r
9297 CloseHandle(piProcInfo.hThread);
\r
9298 CloseHandle(hChildStdinRd);
\r
9299 CloseHandle(hChildStdoutWr);
\r
9301 /* Prepare return value */
\r
9302 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9303 cp->kind = CPReal;
\r
9304 cp->hProcess = piProcInfo.hProcess;
\r
9305 cp->pid = piProcInfo.dwProcessId;
\r
9306 cp->hFrom = hChildStdoutRdDup;
\r
9307 cp->hTo = hChildStdinWrDup;
\r
9309 *pr = (void *) cp;
\r
9311 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
9312 2000 where engines sometimes don't see the initial command(s)
\r
9313 from WinBoard and hang. I don't understand how that can happen,
\r
9314 but the Sleep is harmless, so I've put it in. Others have also
\r
9315 reported what may be the same problem, so hopefully this will fix
\r
9316 it for them too. */
\r
9324 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
9326 ChildProc *cp; int result;
\r
9328 cp = (ChildProc *) pr;
\r
9329 if (cp == NULL) return;
\r
9331 switch (cp->kind) {
\r
9333 /* TerminateProcess is considered harmful, so... */
\r
9334 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9335 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9336 /* The following doesn't work because the chess program
\r
9337 doesn't "have the same console" as WinBoard. Maybe
\r
9338 we could arrange for this even though neither WinBoard
\r
9339 nor the chess program uses a console for stdio? */
\r
9340 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9342 /* [AS] Special termination modes for misbehaving programs... */
\r
9343 if( signal & 8 ) {
\r
9344 result = TerminateProcess( cp->hProcess, 0 );
\r
9346 if ( appData.debugMode) {
\r
9347 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9350 else if( signal & 4 ) {
\r
9351 DWORD dw = WaitForSingleObject( cp->hProcess, appData.delayAfterQuit*1000 + 50 ); // Wait 3 seconds at most
\r
9353 if( dw != WAIT_OBJECT_0 ) {
\r
9354 result = TerminateProcess( cp->hProcess, 0 );
\r
9356 if ( appData.debugMode) {
\r
9357 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9363 CloseHandle(cp->hProcess);
\r
9367 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9371 closesocket(cp->sock);
\r
9376 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9377 closesocket(cp->sock);
\r
9378 closesocket(cp->sock2);
\r
9386 InterruptChildProcess(ProcRef pr)
\r
9390 cp = (ChildProc *) pr;
\r
9391 if (cp == NULL) return;
\r
9392 switch (cp->kind) {
\r
9394 /* The following doesn't work because the chess program
\r
9395 doesn't "have the same console" as WinBoard. Maybe
\r
9396 we could arrange for this even though neither WinBoard
\r
9397 nor the chess program uses a console for stdio */
\r
9398 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9403 /* Can't interrupt */
\r
9407 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9414 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9416 char cmdLine[MSG_SIZ];
\r
9418 if (port[0] == NULLCHAR) {
\r
9419 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9421 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9423 return StartChildProcess(cmdLine, "", pr);
\r
9427 /* Code to open TCP sockets */
\r
9430 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9436 struct sockaddr_in sa, mysa;
\r
9437 struct hostent FAR *hp;
\r
9438 unsigned short uport;
\r
9439 WORD wVersionRequested;
\r
9442 /* Initialize socket DLL */
\r
9443 wVersionRequested = MAKEWORD(1, 1);
\r
9444 err = WSAStartup(wVersionRequested, &wsaData);
\r
9445 if (err != 0) return err;
\r
9448 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9449 err = WSAGetLastError();
\r
9454 /* Bind local address using (mostly) don't-care values.
\r
9456 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9457 mysa.sin_family = AF_INET;
\r
9458 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9459 uport = (unsigned short) 0;
\r
9460 mysa.sin_port = htons(uport);
\r
9461 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9462 == SOCKET_ERROR) {
\r
9463 err = WSAGetLastError();
\r
9468 /* Resolve remote host name */
\r
9469 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9470 if (!(hp = gethostbyname(host))) {
\r
9471 unsigned int b0, b1, b2, b3;
\r
9473 err = WSAGetLastError();
\r
9475 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9476 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9477 hp->h_addrtype = AF_INET;
\r
9479 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9480 hp->h_addr_list[0] = (char *) malloc(4);
\r
9481 hp->h_addr_list[0][0] = (char) b0;
\r
9482 hp->h_addr_list[0][1] = (char) b1;
\r
9483 hp->h_addr_list[0][2] = (char) b2;
\r
9484 hp->h_addr_list[0][3] = (char) b3;
\r
9490 sa.sin_family = hp->h_addrtype;
\r
9491 uport = (unsigned short) atoi(port);
\r
9492 sa.sin_port = htons(uport);
\r
9493 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9495 /* Make connection */
\r
9496 if (connect(s, (struct sockaddr *) &sa,
\r
9497 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9498 err = WSAGetLastError();
\r
9503 /* Prepare return value */
\r
9504 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9505 cp->kind = CPSock;
\r
9507 *pr = (ProcRef *) cp;
\r
9513 OpenCommPort(char *name, ProcRef *pr)
\r
9518 char fullname[MSG_SIZ];
\r
9520 if (*name != '\\')
\r
9521 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9523 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9525 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9526 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9527 if (h == (HANDLE) -1) {
\r
9528 return GetLastError();
\r
9532 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9534 /* Accumulate characters until a 100ms pause, then parse */
\r
9535 ct.ReadIntervalTimeout = 100;
\r
9536 ct.ReadTotalTimeoutMultiplier = 0;
\r
9537 ct.ReadTotalTimeoutConstant = 0;
\r
9538 ct.WriteTotalTimeoutMultiplier = 0;
\r
9539 ct.WriteTotalTimeoutConstant = 0;
\r
9540 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9542 /* Prepare return value */
\r
9543 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9544 cp->kind = CPComm;
\r
9547 *pr = (ProcRef *) cp;
\r
9553 OpenLoopback(ProcRef *pr)
\r
9555 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9561 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9566 struct sockaddr_in sa, mysa;
\r
9567 struct hostent FAR *hp;
\r
9568 unsigned short uport;
\r
9569 WORD wVersionRequested;
\r
9572 char stderrPortStr[MSG_SIZ];
\r
9574 /* Initialize socket DLL */
\r
9575 wVersionRequested = MAKEWORD(1, 1);
\r
9576 err = WSAStartup(wVersionRequested, &wsaData);
\r
9577 if (err != 0) return err;
\r
9579 /* Resolve remote host name */
\r
9580 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9581 if (!(hp = gethostbyname(host))) {
\r
9582 unsigned int b0, b1, b2, b3;
\r
9584 err = WSAGetLastError();
\r
9586 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9587 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9588 hp->h_addrtype = AF_INET;
\r
9590 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9591 hp->h_addr_list[0] = (char *) malloc(4);
\r
9592 hp->h_addr_list[0][0] = (char) b0;
\r
9593 hp->h_addr_list[0][1] = (char) b1;
\r
9594 hp->h_addr_list[0][2] = (char) b2;
\r
9595 hp->h_addr_list[0][3] = (char) b3;
\r
9601 sa.sin_family = hp->h_addrtype;
\r
9602 uport = (unsigned short) 514;
\r
9603 sa.sin_port = htons(uport);
\r
9604 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9606 /* Bind local socket to unused "privileged" port address
\r
9608 s = INVALID_SOCKET;
\r
9609 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9610 mysa.sin_family = AF_INET;
\r
9611 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9612 for (fromPort = 1023;; fromPort--) {
\r
9613 if (fromPort < 0) {
\r
9615 return WSAEADDRINUSE;
\r
9617 if (s == INVALID_SOCKET) {
\r
9618 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9619 err = WSAGetLastError();
\r
9624 uport = (unsigned short) fromPort;
\r
9625 mysa.sin_port = htons(uport);
\r
9626 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9627 == SOCKET_ERROR) {
\r
9628 err = WSAGetLastError();
\r
9629 if (err == WSAEADDRINUSE) continue;
\r
9633 if (connect(s, (struct sockaddr *) &sa,
\r
9634 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9635 err = WSAGetLastError();
\r
9636 if (err == WSAEADDRINUSE) {
\r
9647 /* Bind stderr local socket to unused "privileged" port address
\r
9649 s2 = INVALID_SOCKET;
\r
9650 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9651 mysa.sin_family = AF_INET;
\r
9652 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9653 for (fromPort = 1023;; fromPort--) {
\r
9654 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9655 if (fromPort < 0) {
\r
9656 (void) closesocket(s);
\r
9658 return WSAEADDRINUSE;
\r
9660 if (s2 == INVALID_SOCKET) {
\r
9661 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9662 err = WSAGetLastError();
\r
9668 uport = (unsigned short) fromPort;
\r
9669 mysa.sin_port = htons(uport);
\r
9670 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9671 == SOCKET_ERROR) {
\r
9672 err = WSAGetLastError();
\r
9673 if (err == WSAEADDRINUSE) continue;
\r
9674 (void) closesocket(s);
\r
9678 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9679 err = WSAGetLastError();
\r
9680 if (err == WSAEADDRINUSE) {
\r
9682 s2 = INVALID_SOCKET;
\r
9685 (void) closesocket(s);
\r
9686 (void) closesocket(s2);
\r
9692 prevStderrPort = fromPort; // remember port used
\r
9693 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9695 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9696 err = WSAGetLastError();
\r
9697 (void) closesocket(s);
\r
9698 (void) closesocket(s2);
\r
9703 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9704 err = WSAGetLastError();
\r
9705 (void) closesocket(s);
\r
9706 (void) closesocket(s2);
\r
9710 if (*user == NULLCHAR) user = UserName();
\r
9711 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9712 err = WSAGetLastError();
\r
9713 (void) closesocket(s);
\r
9714 (void) closesocket(s2);
\r
9718 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9719 err = WSAGetLastError();
\r
9720 (void) closesocket(s);
\r
9721 (void) closesocket(s2);
\r
9726 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9727 err = WSAGetLastError();
\r
9728 (void) closesocket(s);
\r
9729 (void) closesocket(s2);
\r
9733 (void) closesocket(s2); /* Stop listening */
\r
9735 /* Prepare return value */
\r
9736 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9737 cp->kind = CPRcmd;
\r
9740 *pr = (ProcRef *) cp;
\r
9747 AddInputSource(ProcRef pr, int lineByLine,
\r
9748 InputCallback func, VOIDSTAR closure)
\r
9750 InputSource *is, *is2 = NULL;
\r
9751 ChildProc *cp = (ChildProc *) pr;
\r
9753 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9754 is->lineByLine = lineByLine;
\r
9756 is->closure = closure;
\r
9757 is->second = NULL;
\r
9758 is->next = is->buf;
\r
9759 if (pr == NoProc) {
\r
9760 is->kind = CPReal;
\r
9761 consoleInputSource = is;
\r
9763 is->kind = cp->kind;
\r
9765 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9766 we create all threads suspended so that the is->hThread variable can be
\r
9767 safely assigned, then let the threads start with ResumeThread.
\r
9769 switch (cp->kind) {
\r
9771 is->hFile = cp->hFrom;
\r
9772 cp->hFrom = NULL; /* now owned by InputThread */
\r
9774 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9775 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9779 is->hFile = cp->hFrom;
\r
9780 cp->hFrom = NULL; /* now owned by InputThread */
\r
9782 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9783 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9787 is->sock = cp->sock;
\r
9789 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9790 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9794 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9796 is->sock = cp->sock;
\r
9798 is2->sock = cp->sock2;
\r
9799 is2->second = is2;
\r
9801 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9802 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9804 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9805 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9809 if( is->hThread != NULL ) {
\r
9810 ResumeThread( is->hThread );
\r
9813 if( is2 != NULL && is2->hThread != NULL ) {
\r
9814 ResumeThread( is2->hThread );
\r
9818 return (InputSourceRef) is;
\r
9822 RemoveInputSource(InputSourceRef isr)
\r
9826 is = (InputSource *) isr;
\r
9827 is->hThread = NULL; /* tell thread to stop */
\r
9828 CloseHandle(is->hThread);
\r
9829 if (is->second != NULL) {
\r
9830 is->second->hThread = NULL;
\r
9831 CloseHandle(is->second->hThread);
\r
9835 int no_wrap(char *message, int count)
\r
9837 ConsoleOutput(message, count, FALSE);
\r
9842 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9845 int outCount = SOCKET_ERROR;
\r
9846 ChildProc *cp = (ChildProc *) pr;
\r
9847 static OVERLAPPED ovl;
\r
9849 static int line = 0;
\r
9853 if (appData.noJoin || !appData.useInternalWrap)
\r
9854 return no_wrap(message, count);
\r
9857 int width = get_term_width();
\r
9858 int len = wrap(NULL, message, count, width, &line);
\r
9859 char *msg = malloc(len);
\r
9863 return no_wrap(message, count);
\r
9866 dbgchk = wrap(msg, message, count, width, &line);
\r
9867 if (dbgchk != len && appData.debugMode)
\r
9868 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9869 ConsoleOutput(msg, len, FALSE);
\r
9876 if (ovl.hEvent == NULL) {
\r
9877 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9879 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9881 switch (cp->kind) {
\r
9884 outCount = send(cp->sock, message, count, 0);
\r
9885 if (outCount == SOCKET_ERROR) {
\r
9886 *outError = WSAGetLastError();
\r
9888 *outError = NO_ERROR;
\r
9893 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9894 &dOutCount, NULL)) {
\r
9895 *outError = NO_ERROR;
\r
9896 outCount = (int) dOutCount;
\r
9898 *outError = GetLastError();
\r
9903 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9904 &dOutCount, &ovl);
\r
9905 if (*outError == NO_ERROR) {
\r
9906 outCount = (int) dOutCount;
\r
9916 if(n != 0) Sleep(n);
\r
9920 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9923 /* Ignore delay, not implemented for WinBoard */
\r
9924 return OutputToProcess(pr, message, count, outError);
\r
9929 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9930 char *buf, int count, int error)
\r
9932 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9935 /* see wgamelist.c for Game List functions */
\r
9936 /* see wedittags.c for Edit Tags functions */
\r
9943 char buf[MSG_SIZ];
\r
9946 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9947 f = fopen(buf, "r");
\r
9949 ProcessICSInitScript(f);
\r
9959 StartAnalysisClock()
\r
9961 if (analysisTimerEvent) return;
\r
9962 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9963 (UINT) 2000, NULL);
\r
9967 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9969 highlightInfo.sq[0].x = fromX;
\r
9970 highlightInfo.sq[0].y = fromY;
\r
9971 highlightInfo.sq[1].x = toX;
\r
9972 highlightInfo.sq[1].y = toY;
\r
9978 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9979 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9983 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9985 premoveHighlightInfo.sq[0].x = fromX;
\r
9986 premoveHighlightInfo.sq[0].y = fromY;
\r
9987 premoveHighlightInfo.sq[1].x = toX;
\r
9988 premoveHighlightInfo.sq[1].y = toY;
\r
9992 ClearPremoveHighlights()
\r
9994 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9995 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9999 ShutDownFrontEnd()
\r
10001 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
10002 DeleteClipboardTempFiles();
\r
10008 if (IsIconic(hwndMain))
\r
10009 ShowWindow(hwndMain, SW_RESTORE);
\r
10011 SetActiveWindow(hwndMain);
\r
10015 * Prototypes for animation support routines
\r
10017 static void ScreenSquare(int column, int row, POINT * pt);
\r
10018 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
10019 POINT frames[], int * nFrames);
\r
10022 #define kFactor 4
\r
10025 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
10026 { // [HGM] atomic: animate blast wave
\r
10029 explodeInfo.fromX = fromX;
\r
10030 explodeInfo.fromY = fromY;
\r
10031 explodeInfo.toX = toX;
\r
10032 explodeInfo.toY = toY;
\r
10033 for(i=1; i<4*kFactor; i++) {
\r
10034 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
10035 DrawPosition(FALSE, board);
\r
10036 Sleep(appData.animSpeed);
\r
10038 explodeInfo.radius = 0;
\r
10039 DrawPosition(TRUE, board);
\r
10043 AnimateMove(board, fromX, fromY, toX, toY)
\r
10050 ChessSquare piece;
\r
10051 int x = toX, y = toY;
\r
10052 POINT start, finish, mid;
\r
10053 POINT frames[kFactor * 2 + 1];
\r
10056 if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();
\r
10058 if (!appData.animate) return;
\r
10059 if (doingSizing) return;
\r
10060 if (fromY < 0 || fromX < 0) return;
\r
10061 piece = board[fromY][fromX];
\r
10062 if (piece >= EmptySquare) return;
\r
10064 if(killX >= 0) toX = killX, toY = killY; // [HGM] lion: first to kill square
\r
10068 ScreenSquare(fromX, fromY, &start);
\r
10069 ScreenSquare(toX, toY, &finish);
\r
10071 /* All moves except knight jumps move in straight line */
\r
10072 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
10073 mid.x = start.x + (finish.x - start.x) / 2;
\r
10074 mid.y = start.y + (finish.y - start.y) / 2;
\r
10076 /* Knight: make straight movement then diagonal */
\r
10077 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
10078 mid.x = start.x + (finish.x - start.x) / 2;
\r
10082 mid.y = start.y + (finish.y - start.y) / 2;
\r
10086 /* Don't use as many frames for very short moves */
\r
10087 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
10088 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
10090 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
10092 animInfo.from.x = fromX;
\r
10093 animInfo.from.y = fromY;
\r
10094 animInfo.to.x = toX;
\r
10095 animInfo.to.y = toY;
\r
10096 animInfo.lastpos = start;
\r
10097 animInfo.piece = piece;
\r
10098 for (n = 0; n < nFrames; n++) {
\r
10099 animInfo.pos = frames[n];
\r
10100 DrawPosition(FALSE, NULL);
\r
10101 animInfo.lastpos = animInfo.pos;
\r
10102 Sleep(appData.animSpeed);
\r
10104 animInfo.pos = finish;
\r
10105 DrawPosition(FALSE, NULL);
\r
10107 if(toX != x || toY != y) { fromX = toX; fromY = toY; toX = x; toY = y; goto again; } // second leg
\r
10109 animInfo.piece = EmptySquare;
\r
10110 Explode(board, fromX, fromY, toX, toY);
\r
10113 /* Convert board position to corner of screen rect and color */
\r
10116 ScreenSquare(column, row, pt)
\r
10117 int column; int row; POINT * pt;
\r
10120 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
10121 pt->y = lineGap + row * (squareSize + lineGap) + border;
\r
10123 pt->x = lineGap + column * (squareSize + lineGap) + border;
\r
10124 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
10128 /* Generate a series of frame coords from start->mid->finish.
\r
10129 The movement rate doubles until the half way point is
\r
10130 reached, then halves back down to the final destination,
\r
10131 which gives a nice slow in/out effect. The algorithmn
\r
10132 may seem to generate too many intermediates for short
\r
10133 moves, but remember that the purpose is to attract the
\r
10134 viewers attention to the piece about to be moved and
\r
10135 then to where it ends up. Too few frames would be less
\r
10139 Tween(start, mid, finish, factor, frames, nFrames)
\r
10140 POINT * start; POINT * mid;
\r
10141 POINT * finish; int factor;
\r
10142 POINT frames[]; int * nFrames;
\r
10144 int n, fraction = 1, count = 0;
\r
10146 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
10147 for (n = 0; n < factor; n++)
\r
10149 for (n = 0; n < factor; n++) {
\r
10150 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
10151 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
10153 fraction = fraction / 2;
\r
10157 frames[count] = *mid;
\r
10160 /* Slow out, stepping 1/2, then 1/4, ... */
\r
10162 for (n = 0; n < factor; n++) {
\r
10163 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
10164 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
10166 fraction = fraction * 2;
\r
10168 *nFrames = count;
\r
10172 SettingsPopUp(ChessProgramState *cps)
\r
10173 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
10174 EngineOptionsPopup(savedHwnd, cps);
\r
10177 int flock(int fid, int code)
\r
10179 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
10181 ov.hEvent = NULL;
\r
10183 ov.OffsetHigh = 0;
\r
10185 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
10187 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
10188 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
10189 default: return -1;
\r
10198 static char col[8][20];
\r
10199 COLORREF color = *(COLORREF *) colorVariable[n];
\r
10201 snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
10206 ActivateTheme (int new)
\r
10207 { // Redo initialization of features depending on options that can occur in themes
\r
10209 if(new) InitDrawingColors();
\r
10210 fontBitmapSquareSize = 0; // request creation of new font pieces
\r
10211 InitDrawingSizes(boardSize, 0);
\r
10212 InvalidateRect(hwndMain, NULL, TRUE);
\r