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 Free Software Foundation, Inc.
\r
10 * Enhancements Copyright 2005 Alessandro Scotti
\r
12 * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,
\r
13 * which was written and is copyrighted by Wayne Christopher.
\r
15 * The following terms apply to Digital Equipment Corporation's copyright
\r
16 * interest in XBoard:
\r
17 * ------------------------------------------------------------------------
\r
18 * All Rights Reserved
\r
20 * Permission to use, copy, modify, and distribute this software and its
\r
21 * documentation for any purpose and without fee is hereby granted,
\r
22 * provided that the above copyright notice appear in all copies and that
\r
23 * both that copyright notice and this permission notice appear in
\r
24 * supporting documentation, and that the name of Digital not be
\r
25 * used in advertising or publicity pertaining to distribution of the
\r
26 * software without specific, written prior permission.
\r
28 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
\r
29 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
\r
30 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
\r
31 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
\r
32 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
\r
33 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
\r
35 * ------------------------------------------------------------------------
\r
37 * The following terms apply to the enhanced version of XBoard
\r
38 * distributed by the Free Software Foundation:
\r
39 * ------------------------------------------------------------------------
\r
41 * GNU XBoard is free software: you can redistribute it and/or modify
\r
42 * it under the terms of the GNU General Public License as published by
\r
43 * the Free Software Foundation, either version 3 of the License, or (at
\r
44 * your option) any later version.
\r
46 * GNU XBoard is distributed in the hope that it will be useful, but
\r
47 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
48 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
49 * General Public License for more details.
\r
51 * You should have received a copy of the GNU General Public License
\r
52 * along with this program. If not, see http://www.gnu.org/licenses/. *
\r
54 *------------------------------------------------------------------------
\r
55 ** See the file ChangeLog for a revision history. */
\r
59 #include <windows.h>
\r
60 #include <winuser.h>
\r
61 #include <winsock.h>
\r
62 #include <commctrl.h>
\r
68 #include <sys/stat.h>
\r
71 #include <commdlg.h>
\r
73 #include <richedit.h>
\r
74 #include <mmsystem.h>
\r
83 #include "frontend.h"
\r
84 #include "backend.h"
\r
85 #include "winboard.h"
\r
87 #include "wclipbrd.h"
\r
88 #include "woptions.h"
\r
89 #include "wsockerr.h"
\r
90 #include "defaults.h"
\r
94 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
\r
97 void mysrandom(unsigned int seed);
\r
99 extern int whiteFlag, blackFlag;
\r
100 Boolean flipClock = FALSE;
\r
101 extern HANDLE chatHandle[];
\r
102 extern int ics_type;
\r
104 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
105 VOID NewVariantPopup(HWND hwnd);
\r
106 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
107 /*char*/int promoChar));
\r
108 void AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames);
\r
109 void DisplayMove P((int moveNumber));
\r
110 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
\r
111 void ChatPopUp P((char *s));
\r
113 ChessSquare piece;
\r
114 POINT pos; /* window coordinates of current pos */
\r
115 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
116 POINT from; /* board coordinates of the piece's orig pos */
\r
117 POINT to; /* board coordinates of the piece's new pos */
\r
120 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
123 POINT start; /* window coordinates of start pos */
\r
124 POINT pos; /* window coordinates of current pos */
\r
125 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
126 POINT from; /* board coordinates of the piece's orig pos */
\r
129 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };
\r
132 POINT sq[2]; /* board coordinates of from, to squares */
\r
135 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
136 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
137 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
138 static HighlightInfo oldPartnerHighlight = { {{-1, -1}, {-1, -1}} };
\r
140 typedef struct { // [HGM] atomic
\r
141 int fromX, fromY, toX, toY, radius;
\r
144 static ExplodeInfo explodeInfo;
\r
146 /* Window class names */
\r
147 char szAppName[] = "WinBoard";
\r
148 char szConsoleName[] = "WBConsole";
\r
150 /* Title bar text */
\r
151 char szTitle[] = "WinBoard";
\r
152 char szConsoleTitle[] = "I C S Interaction";
\r
155 char *settingsFileName;
\r
156 Boolean saveSettingsOnExit;
\r
157 char installDir[MSG_SIZ];
\r
158 int errorExitStatus;
\r
160 BoardSize boardSize;
\r
161 Boolean chessProgram;
\r
162 //static int boardX, boardY;
\r
163 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
164 int squareSize, lineGap, minorSize;
\r
165 static int winW, winH;
\r
166 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
167 static int logoHeight = 0;
\r
168 static char messageText[MESSAGE_TEXT_MAX];
\r
169 static int clockTimerEvent = 0;
\r
170 static int loadGameTimerEvent = 0;
\r
171 static int analysisTimerEvent = 0;
\r
172 static DelayedEventCallback delayedTimerCallback;
\r
173 static int delayedTimerEvent = 0;
\r
174 static int buttonCount = 2;
\r
175 char *icsTextMenuString;
\r
177 char *firstChessProgramNames;
\r
178 char *secondChessProgramNames;
\r
180 #define PALETTESIZE 256
\r
182 HINSTANCE hInst; /* current instance */
\r
183 Boolean alwaysOnTop = FALSE;
\r
185 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
186 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
188 ColorClass currentColorClass;
\r
190 HWND hCommPort = NULL; /* currently open comm port */
\r
191 static HWND hwndPause; /* pause button */
\r
192 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
193 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
194 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
195 explodeBrush, /* [HGM] atomic */
\r
196 markerBrush, /* [HGM] markers */
\r
197 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
198 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
199 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
200 static HPEN gridPen = NULL;
\r
201 static HPEN highlightPen = NULL;
\r
202 static HPEN premovePen = NULL;
\r
203 static NPLOGPALETTE pLogPal;
\r
204 static BOOL paletteChanged = FALSE;
\r
205 static HICON iconWhite, iconBlack, iconCurrent;
\r
206 static int doingSizing = FALSE;
\r
207 static int lastSizing = 0;
\r
208 static int prevStderrPort;
\r
209 static HBITMAP userLogo;
\r
211 static HBITMAP liteBackTexture = NULL;
\r
212 static HBITMAP darkBackTexture = NULL;
\r
213 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
214 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
215 static int backTextureSquareSize = 0;
\r
216 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
218 #if __GNUC__ && !defined(_winmajor)
\r
219 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
221 #if defined(_winmajor)
\r
222 #define oldDialog (_winmajor < 4)
\r
224 #define oldDialog 0
\r
228 #define INTERNATIONAL
\r
230 #ifdef INTERNATIONAL
\r
231 # define _(s) T_(s)
\r
237 # define Translate(x, y)
\r
238 # define LoadLanguageFile(s)
\r
241 #ifdef INTERNATIONAL
\r
243 Boolean barbaric; // flag indicating if translation is needed
\r
245 // list of item numbers used in each dialog (used to alter language at run time)
\r
247 #define ABOUTBOX -1 /* not sure why these are needed */
\r
248 #define ABOUTBOX2 -1
\r
250 int dialogItems[][40] = {
\r
251 { ABOUTBOX, IDOK, 400 },
\r
252 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed,
\r
253 OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors, IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL },
\r
254 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, IDOK, IDCANCEL },
\r
255 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
256 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
257 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
258 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
259 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
260 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
261 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
262 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
263 { ABOUTBOX2, IDC_ChessBoard },
\r
264 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
265 OPT_GameListClose, IDC_GameListDoFilter },
\r
266 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
267 { DLG_Error, IDOK },
\r
268 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
269 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
270 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
271 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
272 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
273 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
274 { DLG_IndexNumber, IDC_Index },
\r
275 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
276 { DLG_TypeInName, IDOK, IDCANCEL },
\r
277 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
278 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
279 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
280 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
281 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
282 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
283 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
284 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
285 OPT_HighlightMoveArrow, OPT_AutoLogo },
\r
286 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
287 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
288 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
289 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
290 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
291 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
292 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
293 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
294 GPB_General, GPB_Alarm },
\r
295 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
296 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
297 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
298 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
299 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
300 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
301 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
302 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size },
\r
303 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
304 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
305 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
306 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
307 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
308 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
309 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat,
\r
310 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
311 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
312 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
313 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
314 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont,
\r
315 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
316 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
317 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
318 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
319 { DLG_MoveHistory },
\r
320 { DLG_EvalGraph },
\r
321 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
322 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
323 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
324 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
325 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
326 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
327 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
328 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
329 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
333 char languageBuf[40000], *foreign[1000], *english[1000];
\r
336 LoadLanguageFile(char *name)
\r
337 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
339 int i=0, j=0, n=0, k;
\r
340 static char oldLanguage[MSG_SIZ];
\r
341 if(!strcmp(name, oldLanguage)) return;
\r
342 if(!name || name[0] == NULLCHAR) return;
\r
343 if((f = fopen(name, "r")) == NULL) return;
\r
344 while((k = fgetc(f)) != EOF) {
\r
345 if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }
\r
346 languageBuf[i] = k;
\r
348 if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {
\r
350 if(p = strstr(languageBuf + n + 1, "\" === \"")) {
\r
351 if(p > languageBuf+n+2 && p+8 < languageBuf+i) {
\r
352 if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }
\r
353 english[j] = languageBuf + n + 1; *p = 0;
\r
354 foreign[j++] = p + 7; languageBuf[i-1] = 0;
\r
355 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);
\r
360 } else if(i > 0 && languageBuf[i-1] == '\\') {
\r
362 case 'n': k = '\n'; break;
\r
363 case 'r': k = '\r'; break;
\r
364 case 't': k = '\t'; break;
\r
366 languageBuf[--i] = k;
\r
371 barbaric = (j != 0);
\r
372 strcpy(oldLanguage, buf);
\r
377 { // return the translation of the given string
\r
378 // efficiency can be improved a lot...
\r
380 if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
381 if(!barbaric) return s;
\r
382 if(!s) return ""; // sanity
\r
383 while(english[i]) {
\r
384 if(!strcmp(s, english[i])) return foreign[i];
\r
391 Translate(HWND hDlg, int dialogID)
\r
392 { // translate all text items in the given dialog
\r
394 char buf[MSG_SIZ], *s;
\r
395 //if(appData.debugMode) fprintf(debugFP, "Translate(%d)\n", dialogID);
\r
396 if(!barbaric) return;
\r
397 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
398 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
399 GetWindowText( hDlg, buf, MSG_SIZ );
\r
401 if(appData.debugMode) fprintf(debugFP, "WindowText '%s' -> '%s'\n", buf, s);
\r
402 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
403 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
404 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
405 if(strlen(buf) == 0) continue;
\r
407 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
416 HMENU mainMenu = GetMenu(hwndMain);
\r
417 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
418 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
419 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
421 UINT k = GetMenuItemID(subMenu, j);
\r
422 GetMenuString(subMenu, j, buf, MSG_SIZ, MF_BYPOSITION);
\r
423 if(buf[0] == NULLCHAR) continue;
\r
424 //fprintf(debugFP, "menu(%d,%d) = %s (%08x, %08x) %d\n", i, j, buf, mainMenu, subMenu, k);
\r
425 ModifyMenu(subMenu, j, MF_STRING|MF_BYPOSITION,
\r
441 int cliWidth, cliHeight;
\r
444 SizeInfo sizeInfo[] =
\r
446 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
447 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
448 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
449 { "petite", 33, 1, 1, 1, 0, 0 },
\r
450 { "slim", 37, 2, 1, 0, 0, 0 },
\r
451 { "small", 40, 2, 1, 0, 0, 0 },
\r
452 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
453 { "middling", 49, 2, 0, 0, 0, 0 },
\r
454 { "average", 54, 2, 0, 0, 0, 0 },
\r
455 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
456 { "medium", 64, 3, 0, 0, 0, 0 },
\r
457 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
458 { "large", 80, 3, 0, 0, 0, 0 },
\r
459 { "big", 87, 3, 0, 0, 0, 0 },
\r
460 { "huge", 95, 3, 0, 0, 0, 0 },
\r
461 { "giant", 108, 3, 0, 0, 0, 0 },
\r
462 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
463 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
464 { NULL, 0, 0, 0, 0, 0, 0 }
\r
467 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
468 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
470 { 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) },
\r
471 { 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) },
\r
472 { 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) },
\r
473 { 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) },
\r
474 { 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) },
\r
475 { 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) },
\r
476 { 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) },
\r
477 { 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) },
\r
478 { 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) },
\r
479 { 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) },
\r
480 { 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) },
\r
481 { 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) },
\r
482 { 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) },
\r
483 { 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) },
\r
484 { 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) },
\r
485 { 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) },
\r
486 { 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) },
\r
487 { 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) },
\r
490 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
499 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
500 #define N_BUTTONS 5
\r
502 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
504 {"<<", IDM_ToStart, NULL, NULL},
\r
505 {"<", IDM_Backward, NULL, NULL},
\r
506 {"P", IDM_Pause, NULL, NULL},
\r
507 {">", IDM_Forward, NULL, NULL},
\r
508 {">>", IDM_ToEnd, NULL, NULL},
\r
511 int tinyLayout = 0, smallLayout = 0;
\r
512 #define MENU_BAR_ITEMS 7
\r
513 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
514 { N_("&File"), N_("&Mode"), N_("&Action"), N_("&Step"), N_("&Options"), N_("&Help"), NULL },
\r
515 { N_("&F"), N_("&M"), N_("&A"), N_("&S"), N_("&O"), N_("&H"), NULL },
\r
519 MySound sounds[(int)NSoundClasses];
\r
520 MyTextAttribs textAttribs[(int)NColorClasses];
\r
522 MyColorizeAttribs colorizeAttribs[] = {
\r
523 { (COLORREF)0, 0, N_("Shout Text") },
\r
524 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
525 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
526 { (COLORREF)0, 0, N_("Channel Text") },
\r
527 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
528 { (COLORREF)0, 0, N_("Tell Text") },
\r
529 { (COLORREF)0, 0, N_("Challenge Text") },
\r
530 { (COLORREF)0, 0, N_("Request Text") },
\r
531 { (COLORREF)0, 0, N_("Seek Text") },
\r
532 { (COLORREF)0, 0, N_("Normal Text") },
\r
533 { (COLORREF)0, 0, N_("None") }
\r
538 static char *commentTitle;
\r
539 static char *commentText;
\r
540 static int commentIndex;
\r
541 static Boolean editComment = FALSE;
\r
544 char errorTitle[MSG_SIZ];
\r
545 char errorMessage[2*MSG_SIZ];
\r
546 HWND errorDialog = NULL;
\r
547 BOOLEAN moveErrorMessageUp = FALSE;
\r
548 BOOLEAN consoleEcho = TRUE;
\r
549 CHARFORMAT consoleCF;
\r
550 COLORREF consoleBackgroundColor;
\r
552 char *programVersion;
\r
558 typedef int CPKind;
\r
567 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
570 #define INPUT_SOURCE_BUF_SIZE 4096
\r
572 typedef struct _InputSource {
\r
579 char buf[INPUT_SOURCE_BUF_SIZE];
\r
583 InputCallback func;
\r
584 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
588 InputSource *consoleInputSource;
\r
593 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
594 VOID ConsoleCreate();
\r
596 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
597 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
598 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
599 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
601 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
602 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
603 void ParseIcsTextMenu(char *icsTextMenuString);
\r
604 VOID PopUpMoveDialog(char firstchar);
\r
605 VOID PopUpNameDialog(char firstchar);
\r
606 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
610 int GameListOptions();
\r
612 int dummy; // [HGM] for obsolete args
\r
614 HWND hwndMain = NULL; /* root window*/
\r
615 HWND hwndConsole = NULL;
\r
616 HWND commentDialog = NULL;
\r
617 HWND moveHistoryDialog = NULL;
\r
618 HWND evalGraphDialog = NULL;
\r
619 HWND engineOutputDialog = NULL;
\r
620 HWND gameListDialog = NULL;
\r
621 HWND editTagsDialog = NULL;
\r
623 int commentUp = FALSE;
\r
625 WindowPlacement wpMain;
\r
626 WindowPlacement wpConsole;
\r
627 WindowPlacement wpComment;
\r
628 WindowPlacement wpMoveHistory;
\r
629 WindowPlacement wpEvalGraph;
\r
630 WindowPlacement wpEngineOutput;
\r
631 WindowPlacement wpGameList;
\r
632 WindowPlacement wpTags;
\r
634 VOID EngineOptionsPopup(); // [HGM] settings
\r
636 VOID GothicPopUp(char *title, VariantClass variant);
\r
638 * Setting "frozen" should disable all user input other than deleting
\r
639 * the window. We do this while engines are initializing themselves.
\r
641 static int frozen = 0;
\r
642 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
648 if (frozen) return;
\r
650 hmenu = GetMenu(hwndMain);
\r
651 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
652 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
654 DrawMenuBar(hwndMain);
\r
657 /* Undo a FreezeUI */
\r
663 if (!frozen) return;
\r
665 hmenu = GetMenu(hwndMain);
\r
666 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
667 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
669 DrawMenuBar(hwndMain);
\r
672 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
674 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
680 #define JAWS_ALT_INTERCEPT
\r
681 #define JAWS_KB_NAVIGATION
\r
682 #define JAWS_MENU_ITEMS
\r
683 #define JAWS_SILENCE
\r
684 #define JAWS_REPLAY
\r
686 #define JAWS_COPYRIGHT
\r
687 #define JAWS_DELETE(X) X
\r
688 #define SAYMACHINEMOVE()
\r
692 /*---------------------------------------------------------------------------*\
\r
696 \*---------------------------------------------------------------------------*/
\r
699 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
700 LPSTR lpCmdLine, int nCmdShow)
\r
703 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
704 // INITCOMMONCONTROLSEX ex;
\r
708 LoadLibrary("RICHED32.DLL");
\r
709 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
711 if (!InitApplication(hInstance)) {
\r
714 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
720 // InitCommonControlsEx(&ex);
\r
721 InitCommonControls();
\r
723 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
724 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
725 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
727 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
729 while (GetMessage(&msg, /* message structure */
\r
730 NULL, /* handle of window receiving the message */
\r
731 0, /* lowest message to examine */
\r
732 0)) /* highest message to examine */
\r
735 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
736 // [HGM] navigate: switch between all windows with tab
\r
737 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
738 int i, currentElement = 0;
\r
740 // first determine what element of the chain we come from (if any)
\r
741 if(appData.icsActive) {
\r
742 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
743 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
745 if(engineOutputDialog && EngineOutputIsUp()) {
\r
746 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
747 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
749 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
750 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
752 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
753 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
754 if(msg.hwnd == e1) currentElement = 2; else
\r
755 if(msg.hwnd == e2) currentElement = 3; else
\r
756 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
757 if(msg.hwnd == mh) currentElement = 4; else
\r
758 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
759 if(msg.hwnd == hText) currentElement = 5; else
\r
760 if(msg.hwnd == hInput) currentElement = 6; else
\r
761 for (i = 0; i < N_BUTTONS; i++) {
\r
762 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
765 // determine where to go to
\r
766 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
768 currentElement = (currentElement + direction) % 7;
\r
769 switch(currentElement) {
\r
771 h = hwndMain; break; // passing this case always makes the loop exit
\r
773 h = buttonDesc[0].hwnd; break; // could be NULL
\r
775 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
778 if(!EngineOutputIsUp()) continue;
\r
781 if(!MoveHistoryIsUp()) continue;
\r
783 // case 6: // input to eval graph does not seem to get here!
\r
784 // if(!EvalGraphIsUp()) continue;
\r
785 // h = evalGraphDialog; break;
\r
787 if(!appData.icsActive) continue;
\r
791 if(!appData.icsActive) continue;
\r
797 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
798 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
801 continue; // this message now has been processed
\r
805 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
806 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
807 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
808 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
809 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
810 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
811 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
812 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
813 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
814 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
815 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
816 for(i=0; i<MAX_CHAT; i++)
\r
817 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
820 if(done) continue; // [HGM] chat: end patch
\r
821 TranslateMessage(&msg); /* Translates virtual key codes */
\r
822 DispatchMessage(&msg); /* Dispatches message to window */
\r
827 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
830 /*---------------------------------------------------------------------------*\
\r
832 * Initialization functions
\r
834 \*---------------------------------------------------------------------------*/
\r
838 { // update user logo if necessary
\r
839 static char oldUserName[MSG_SIZ], *curName;
\r
841 if(appData.autoLogo) {
\r
842 curName = UserName();
\r
843 if(strcmp(curName, oldUserName)) {
\r
844 sprintf(oldUserName, "logos\\%s.bmp", curName);
\r
845 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
846 strcpy(oldUserName, curName);
\r
852 InitApplication(HINSTANCE hInstance)
\r
856 /* Fill in window class structure with parameters that describe the */
\r
859 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
860 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
861 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
862 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
863 wc.hInstance = hInstance; /* Owner of this class */
\r
864 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
865 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
866 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
867 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
868 wc.lpszClassName = szAppName; /* Name to register as */
\r
870 /* Register the window class and return success/failure code. */
\r
871 if (!RegisterClass(&wc)) return FALSE;
\r
873 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
874 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
876 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
877 wc.hInstance = hInstance;
\r
878 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
879 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
880 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
881 wc.lpszMenuName = NULL;
\r
882 wc.lpszClassName = szConsoleName;
\r
884 if (!RegisterClass(&wc)) return FALSE;
\r
889 /* Set by InitInstance, used by EnsureOnScreen */
\r
890 int screenHeight, screenWidth;
\r
893 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
895 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
896 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
897 if (*x > screenWidth - 32) *x = 0;
\r
898 if (*y > screenHeight - 32) *y = 0;
\r
899 if (*x < minX) *x = minX;
\r
900 if (*y < minY) *y = minY;
\r
904 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
906 HWND hwnd; /* Main window handle. */
\r
908 WINDOWPLACEMENT wp;
\r
911 hInst = hInstance; /* Store instance handle in our global variable */
\r
912 programName = szAppName;
\r
914 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
915 *filepart = NULLCHAR;
\r
917 GetCurrentDirectory(MSG_SIZ, installDir);
\r
919 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
920 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
921 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
922 /* xboard, and older WinBoards, controlled the move sound with the
\r
923 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
924 always turn the option on (so that the backend will call us),
\r
925 then let the user turn the sound off by setting it to silence if
\r
926 desired. To accommodate old winboard.ini files saved by old
\r
927 versions of WinBoard, we also turn off the sound if the option
\r
928 was initially set to false. [HGM] taken out of InitAppData */
\r
929 if (!appData.ringBellAfterMoves) {
\r
930 sounds[(int)SoundMove].name = strdup("");
\r
931 appData.ringBellAfterMoves = TRUE;
\r
933 if (appData.debugMode) {
\r
934 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
935 setbuf(debugFP, NULL);
\r
938 LoadLanguageFile(appData.language);
\r
942 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
943 // InitEngineUCI( installDir, &second );
\r
945 /* Create a main window for this application instance. */
\r
946 hwnd = CreateWindow(szAppName, szTitle,
\r
947 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
948 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
949 NULL, NULL, hInstance, NULL);
\r
952 /* If window could not be created, return "failure" */
\r
957 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
958 if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {
\r
959 first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
961 if (first.programLogo == NULL && appData.debugMode) {
\r
962 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );
\r
964 } else if(appData.autoLogo) {
\r
965 if(appData.firstDirectory && appData.firstDirectory[0]) {
\r
967 sprintf(buf, "%s/logo.bmp", appData.firstDirectory);
\r
968 first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
972 if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {
\r
973 second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
975 if (second.programLogo == NULL && appData.debugMode) {
\r
976 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );
\r
978 } else if(appData.autoLogo) {
\r
980 if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS
\r
981 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
982 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
984 if(appData.secondDirectory && appData.secondDirectory[0]) {
\r
985 sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);
\r
986 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
992 iconWhite = LoadIcon(hInstance, "icon_white");
\r
993 iconBlack = LoadIcon(hInstance, "icon_black");
\r
994 iconCurrent = iconWhite;
\r
995 InitDrawingColors();
\r
996 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
997 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
998 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
999 /* Compute window size for each board size, and use the largest
\r
1000 size that fits on this screen as the default. */
\r
1001 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1002 if (boardSize == (BoardSize)-1 &&
\r
1003 winH <= screenHeight
\r
1004 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1005 && winW <= screenWidth) {
\r
1006 boardSize = (BoardSize)ibs;
\r
1010 InitDrawingSizes(boardSize, 0);
\r
1013 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1015 /* [AS] Load textures if specified */
\r
1016 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
1018 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1019 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1020 liteBackTextureMode = appData.liteBackTextureMode;
\r
1022 if (liteBackTexture == NULL && appData.debugMode) {
\r
1023 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1027 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1028 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1029 darkBackTextureMode = appData.darkBackTextureMode;
\r
1031 if (darkBackTexture == NULL && appData.debugMode) {
\r
1032 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1036 mysrandom( (unsigned) time(NULL) );
\r
1038 /* [AS] Restore layout */
\r
1039 if( wpMoveHistory.visible ) {
\r
1040 MoveHistoryPopUp();
\r
1043 if( wpEvalGraph.visible ) {
\r
1047 if( wpEngineOutput.visible ) {
\r
1048 EngineOutputPopUp();
\r
1051 /* Make the window visible; update its client area; and return "success" */
\r
1052 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1053 wp.length = sizeof(WINDOWPLACEMENT);
\r
1055 wp.showCmd = nCmdShow;
\r
1056 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1057 wp.rcNormalPosition.left = wpMain.x;
\r
1058 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1059 wp.rcNormalPosition.top = wpMain.y;
\r
1060 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1061 SetWindowPlacement(hwndMain, &wp);
\r
1063 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1065 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1066 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1068 if (hwndConsole) {
\r
1070 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1071 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1073 ShowWindow(hwndConsole, nCmdShow);
\r
1074 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
1075 char buf[MSG_SIZ], *p = buf, *q;
\r
1076 strcpy(buf, appData.chatBoxes);
\r
1078 q = strchr(p, ';');
\r
1080 if(*p) ChatPopUp(p);
\r
1083 SetActiveWindow(hwndConsole);
\r
1085 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1086 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1095 HMENU hmenu = GetMenu(hwndMain);
\r
1097 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1098 MF_BYCOMMAND|((appData.icsActive &&
\r
1099 *appData.icsCommPort != NULLCHAR) ?
\r
1100 MF_ENABLED : MF_GRAYED));
\r
1101 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1102 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1103 MF_CHECKED : MF_UNCHECKED));
\r
1106 //---------------------------------------------------------------------------------------------------------
\r
1108 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1109 #define XBOARD FALSE
\r
1111 #define OPTCHAR "/"
\r
1112 #define SEPCHAR "="
\r
1116 // front-end part of option handling
\r
1119 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1121 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1122 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1125 lf->lfEscapement = 0;
\r
1126 lf->lfOrientation = 0;
\r
1127 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1128 lf->lfItalic = mfp->italic;
\r
1129 lf->lfUnderline = mfp->underline;
\r
1130 lf->lfStrikeOut = mfp->strikeout;
\r
1131 lf->lfCharSet = mfp->charset;
\r
1132 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1133 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1134 lf->lfQuality = DEFAULT_QUALITY;
\r
1135 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1136 strcpy(lf->lfFaceName, mfp->faceName);
\r
1140 CreateFontInMF(MyFont *mf)
\r
1142 LFfromMFP(&mf->lf, &mf->mfp);
\r
1143 if (mf->hf) DeleteObject(mf->hf);
\r
1144 mf->hf = CreateFontIndirect(&mf->lf);
\r
1147 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1149 colorVariable[] = {
\r
1150 &whitePieceColor,
\r
1151 &blackPieceColor,
\r
1152 &lightSquareColor,
\r
1153 &darkSquareColor,
\r
1154 &highlightSquareColor,
\r
1155 &premoveHighlightColor,
\r
1157 &consoleBackgroundColor,
\r
1158 &appData.fontForeColorWhite,
\r
1159 &appData.fontBackColorWhite,
\r
1160 &appData.fontForeColorBlack,
\r
1161 &appData.fontBackColorBlack,
\r
1162 &appData.evalHistColorWhite,
\r
1163 &appData.evalHistColorBlack,
\r
1164 &appData.highlightArrowColor,
\r
1167 /* Command line font name parser. NULL name means do nothing.
\r
1168 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1169 For backward compatibility, syntax without the colon is also
\r
1170 accepted, but font names with digits in them won't work in that case.
\r
1173 ParseFontName(char *name, MyFontParams *mfp)
\r
1176 if (name == NULL) return;
\r
1178 q = strchr(p, ':');
\r
1180 if (q - p >= sizeof(mfp->faceName))
\r
1181 ExitArgError(_("Font name too long:"), name);
\r
1182 memcpy(mfp->faceName, p, q - p);
\r
1183 mfp->faceName[q - p] = NULLCHAR;
\r
1186 q = mfp->faceName;
\r
1187 while (*p && !isdigit(*p)) {
\r
1189 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1190 ExitArgError(_("Font name too long:"), name);
\r
1192 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1195 if (!*p) ExitArgError(_("Font point size missing:"), name);
\r
1196 mfp->pointSize = (float) atof(p);
\r
1197 mfp->bold = (strchr(p, 'b') != NULL);
\r
1198 mfp->italic = (strchr(p, 'i') != NULL);
\r
1199 mfp->underline = (strchr(p, 'u') != NULL);
\r
1200 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1201 mfp->charset = DEFAULT_CHARSET;
\r
1202 q = strchr(p, 'c');
\r
1204 mfp->charset = (BYTE) atoi(q+1);
\r
1208 ParseFont(char *name, int number)
\r
1209 { // wrapper to shield back-end from 'font'
\r
1210 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1215 { // in WB we have a 2D array of fonts; this initializes their description
\r
1217 /* Point font array elements to structures and
\r
1218 parse default font names */
\r
1219 for (i=0; i<NUM_FONTS; i++) {
\r
1220 for (j=0; j<NUM_SIZES; j++) {
\r
1221 font[j][i] = &fontRec[j][i];
\r
1222 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1229 { // here we create the actual fonts from the selected descriptions
\r
1231 for (i=0; i<NUM_FONTS; i++) {
\r
1232 for (j=0; j<NUM_SIZES; j++) {
\r
1233 CreateFontInMF(font[j][i]);
\r
1237 /* Color name parser.
\r
1238 X version accepts X color names, but this one
\r
1239 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1241 ParseColorName(char *name)
\r
1243 int red, green, blue, count;
\r
1244 char buf[MSG_SIZ];
\r
1246 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1248 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1249 &red, &green, &blue);
\r
1252 sprintf(buf, _("Can't parse color name %s"), name);
\r
1253 DisplayError(buf, 0);
\r
1254 return RGB(0, 0, 0);
\r
1256 return PALETTERGB(red, green, blue);
\r
1260 ParseColor(int n, char *name)
\r
1261 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1262 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1266 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1268 char *e = argValue;
\r
1272 if (*e == 'b') eff |= CFE_BOLD;
\r
1273 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1274 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1275 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1276 else if (*e == '#' || isdigit(*e)) break;
\r
1280 *color = ParseColorName(e);
\r
1284 ParseTextAttribs(ColorClass cc, char *s)
\r
1285 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1286 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1287 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1291 ParseBoardSize(void *addr, char *name)
\r
1292 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1293 BoardSize bs = SizeTiny;
\r
1294 while (sizeInfo[bs].name != NULL) {
\r
1295 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1296 *(BoardSize *)addr = bs;
\r
1301 ExitArgError(_("Unrecognized board size value"), name);
\r
1306 { // [HGM] import name from appData first
\r
1309 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1310 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1311 textAttribs[cc].sound.data = NULL;
\r
1312 MyLoadSound(&textAttribs[cc].sound);
\r
1314 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1315 textAttribs[cc].sound.name = strdup("");
\r
1316 textAttribs[cc].sound.data = NULL;
\r
1318 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1319 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1320 sounds[sc].data = NULL;
\r
1321 MyLoadSound(&sounds[sc]);
\r
1326 SetCommPortDefaults()
\r
1328 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1329 dcb.DCBlength = sizeof(DCB);
\r
1330 dcb.BaudRate = 9600;
\r
1331 dcb.fBinary = TRUE;
\r
1332 dcb.fParity = FALSE;
\r
1333 dcb.fOutxCtsFlow = FALSE;
\r
1334 dcb.fOutxDsrFlow = FALSE;
\r
1335 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1336 dcb.fDsrSensitivity = FALSE;
\r
1337 dcb.fTXContinueOnXoff = TRUE;
\r
1338 dcb.fOutX = FALSE;
\r
1340 dcb.fNull = FALSE;
\r
1341 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1342 dcb.fAbortOnError = FALSE;
\r
1344 dcb.Parity = SPACEPARITY;
\r
1345 dcb.StopBits = ONESTOPBIT;
\r
1348 // [HGM] args: these three cases taken out to stay in front-end
\r
1350 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1351 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1352 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1353 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1355 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1356 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1357 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1358 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1359 ad->argName, mfp->faceName, mfp->pointSize,
\r
1360 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1361 mfp->bold ? "b" : "",
\r
1362 mfp->italic ? "i" : "",
\r
1363 mfp->underline ? "u" : "",
\r
1364 mfp->strikeout ? "s" : "",
\r
1365 (int)mfp->charset);
\r
1371 { // [HGM] copy the names from the internal WB variables to appData
\r
1374 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1375 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1376 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1377 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1381 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1382 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1383 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1384 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1385 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1386 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1387 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1388 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1389 (ta->effects) ? " " : "",
\r
1390 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1394 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1395 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1396 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1397 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1398 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1402 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1403 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1404 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1408 ParseCommPortSettings(char *s)
\r
1409 { // wrapper to keep dcb from back-end
\r
1410 ParseCommSettings(s, &dcb);
\r
1415 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1416 GetActualPlacement(hwndMain, &wpMain);
\r
1417 GetActualPlacement(hwndConsole, &wpConsole);
\r
1418 GetActualPlacement(commentDialog, &wpComment);
\r
1419 GetActualPlacement(editTagsDialog, &wpTags);
\r
1420 GetActualPlacement(gameListDialog, &wpGameList);
\r
1421 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1422 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1423 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1427 PrintCommPortSettings(FILE *f, char *name)
\r
1428 { // wrapper to shield back-end from DCB
\r
1429 PrintCommSettings(f, name, &dcb);
\r
1433 MySearchPath(char *installDir, char *name, char *fullname)
\r
1435 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1436 if(name[0]== '%') {
\r
1437 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1438 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1440 *strchr(buf, '%') = 0;
\r
1441 strcat(fullname, getenv(buf));
\r
1442 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1444 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1445 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1446 return (int) strlen(fullname);
\r
1448 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1452 MyGetFullPathName(char *name, char *fullname)
\r
1455 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1460 { // [HGM] args: allows testing if main window is realized from back-end
\r
1461 return hwndMain != NULL;
\r
1465 PopUpStartupDialog()
\r
1469 LoadLanguageFile(appData.language);
\r
1470 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1471 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1472 FreeProcInstance(lpProc);
\r
1475 /*---------------------------------------------------------------------------*\
\r
1477 * GDI board drawing routines
\r
1479 \*---------------------------------------------------------------------------*/
\r
1481 /* [AS] Draw square using background texture */
\r
1482 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1487 return; /* Should never happen! */
\r
1490 SetGraphicsMode( dst, GM_ADVANCED );
\r
1497 /* X reflection */
\r
1502 x.eDx = (FLOAT) dw + dx - 1;
\r
1505 SetWorldTransform( dst, &x );
\r
1508 /* Y reflection */
\r
1514 x.eDy = (FLOAT) dh + dy - 1;
\r
1516 SetWorldTransform( dst, &x );
\r
1524 x.eDx = (FLOAT) dx;
\r
1525 x.eDy = (FLOAT) dy;
\r
1528 SetWorldTransform( dst, &x );
\r
1532 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1540 SetWorldTransform( dst, &x );
\r
1542 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1545 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1547 PM_WP = (int) WhitePawn,
\r
1548 PM_WN = (int) WhiteKnight,
\r
1549 PM_WB = (int) WhiteBishop,
\r
1550 PM_WR = (int) WhiteRook,
\r
1551 PM_WQ = (int) WhiteQueen,
\r
1552 PM_WF = (int) WhiteFerz,
\r
1553 PM_WW = (int) WhiteWazir,
\r
1554 PM_WE = (int) WhiteAlfil,
\r
1555 PM_WM = (int) WhiteMan,
\r
1556 PM_WO = (int) WhiteCannon,
\r
1557 PM_WU = (int) WhiteUnicorn,
\r
1558 PM_WH = (int) WhiteNightrider,
\r
1559 PM_WA = (int) WhiteAngel,
\r
1560 PM_WC = (int) WhiteMarshall,
\r
1561 PM_WAB = (int) WhiteCardinal,
\r
1562 PM_WD = (int) WhiteDragon,
\r
1563 PM_WL = (int) WhiteLance,
\r
1564 PM_WS = (int) WhiteCobra,
\r
1565 PM_WV = (int) WhiteFalcon,
\r
1566 PM_WSG = (int) WhiteSilver,
\r
1567 PM_WG = (int) WhiteGrasshopper,
\r
1568 PM_WK = (int) WhiteKing,
\r
1569 PM_BP = (int) BlackPawn,
\r
1570 PM_BN = (int) BlackKnight,
\r
1571 PM_BB = (int) BlackBishop,
\r
1572 PM_BR = (int) BlackRook,
\r
1573 PM_BQ = (int) BlackQueen,
\r
1574 PM_BF = (int) BlackFerz,
\r
1575 PM_BW = (int) BlackWazir,
\r
1576 PM_BE = (int) BlackAlfil,
\r
1577 PM_BM = (int) BlackMan,
\r
1578 PM_BO = (int) BlackCannon,
\r
1579 PM_BU = (int) BlackUnicorn,
\r
1580 PM_BH = (int) BlackNightrider,
\r
1581 PM_BA = (int) BlackAngel,
\r
1582 PM_BC = (int) BlackMarshall,
\r
1583 PM_BG = (int) BlackGrasshopper,
\r
1584 PM_BAB = (int) BlackCardinal,
\r
1585 PM_BD = (int) BlackDragon,
\r
1586 PM_BL = (int) BlackLance,
\r
1587 PM_BS = (int) BlackCobra,
\r
1588 PM_BV = (int) BlackFalcon,
\r
1589 PM_BSG = (int) BlackSilver,
\r
1590 PM_BK = (int) BlackKing
\r
1593 static HFONT hPieceFont = NULL;
\r
1594 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1595 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1596 static int fontBitmapSquareSize = 0;
\r
1597 static char pieceToFontChar[(int) EmptySquare] =
\r
1598 { 'p', 'n', 'b', 'r', 'q',
\r
1599 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1600 'k', 'o', 'm', 'v', 't', 'w',
\r
1601 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1604 extern BOOL SetCharTable( char *table, const char * map );
\r
1605 /* [HGM] moved to backend.c */
\r
1607 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1610 BYTE r1 = GetRValue( color );
\r
1611 BYTE g1 = GetGValue( color );
\r
1612 BYTE b1 = GetBValue( color );
\r
1618 /* Create a uniform background first */
\r
1619 hbrush = CreateSolidBrush( color );
\r
1620 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1621 FillRect( hdc, &rc, hbrush );
\r
1622 DeleteObject( hbrush );
\r
1625 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1626 int steps = squareSize / 2;
\r
1629 for( i=0; i<steps; i++ ) {
\r
1630 BYTE r = r1 - (r1-r2) * i / steps;
\r
1631 BYTE g = g1 - (g1-g2) * i / steps;
\r
1632 BYTE b = b1 - (b1-b2) * i / steps;
\r
1634 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1635 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1636 FillRect( hdc, &rc, hbrush );
\r
1637 DeleteObject(hbrush);
\r
1640 else if( mode == 2 ) {
\r
1641 /* Diagonal gradient, good more or less for every piece */
\r
1642 POINT triangle[3];
\r
1643 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1644 HBRUSH hbrush_old;
\r
1645 int steps = squareSize;
\r
1648 triangle[0].x = squareSize - steps;
\r
1649 triangle[0].y = squareSize;
\r
1650 triangle[1].x = squareSize;
\r
1651 triangle[1].y = squareSize;
\r
1652 triangle[2].x = squareSize;
\r
1653 triangle[2].y = squareSize - steps;
\r
1655 for( i=0; i<steps; i++ ) {
\r
1656 BYTE r = r1 - (r1-r2) * i / steps;
\r
1657 BYTE g = g1 - (g1-g2) * i / steps;
\r
1658 BYTE b = b1 - (b1-b2) * i / steps;
\r
1660 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1661 hbrush_old = SelectObject( hdc, hbrush );
\r
1662 Polygon( hdc, triangle, 3 );
\r
1663 SelectObject( hdc, hbrush_old );
\r
1664 DeleteObject(hbrush);
\r
1669 SelectObject( hdc, hpen );
\r
1674 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1675 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1676 piece: follow the steps as explained below.
\r
1678 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1682 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1686 int backColor = whitePieceColor;
\r
1687 int foreColor = blackPieceColor;
\r
1689 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1690 backColor = appData.fontBackColorWhite;
\r
1691 foreColor = appData.fontForeColorWhite;
\r
1693 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1694 backColor = appData.fontBackColorBlack;
\r
1695 foreColor = appData.fontForeColorBlack;
\r
1699 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1701 hbm_old = SelectObject( hdc, hbm );
\r
1705 rc.right = squareSize;
\r
1706 rc.bottom = squareSize;
\r
1708 /* Step 1: background is now black */
\r
1709 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1711 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1713 pt.x = (squareSize - sz.cx) / 2;
\r
1714 pt.y = (squareSize - sz.cy) / 2;
\r
1716 SetBkMode( hdc, TRANSPARENT );
\r
1717 SetTextColor( hdc, chroma );
\r
1718 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1719 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1721 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1722 /* Step 3: the area outside the piece is filled with white */
\r
1723 // FloodFill( hdc, 0, 0, chroma );
\r
1724 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1725 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1726 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1727 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1728 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1730 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1731 but if the start point is not inside the piece we're lost!
\r
1732 There should be a better way to do this... if we could create a region or path
\r
1733 from the fill operation we would be fine for example.
\r
1735 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1736 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1738 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1739 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1740 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1742 SelectObject( dc2, bm2 );
\r
1743 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1744 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1745 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1746 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1747 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1750 DeleteObject( bm2 );
\r
1753 SetTextColor( hdc, 0 );
\r
1755 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1756 draw the piece again in black for safety.
\r
1758 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1760 SelectObject( hdc, hbm_old );
\r
1762 if( hPieceMask[index] != NULL ) {
\r
1763 DeleteObject( hPieceMask[index] );
\r
1766 hPieceMask[index] = hbm;
\r
1769 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1771 SelectObject( hdc, hbm );
\r
1774 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1775 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1776 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1778 SelectObject( dc1, hPieceMask[index] );
\r
1779 SelectObject( dc2, bm2 );
\r
1780 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1781 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1784 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1785 the piece background and deletes (makes transparent) the rest.
\r
1786 Thanks to that mask, we are free to paint the background with the greates
\r
1787 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1788 We use this, to make gradients and give the pieces a "roundish" look.
\r
1790 SetPieceBackground( hdc, backColor, 2 );
\r
1791 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1795 DeleteObject( bm2 );
\r
1798 SetTextColor( hdc, foreColor );
\r
1799 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1801 SelectObject( hdc, hbm_old );
\r
1803 if( hPieceFace[index] != NULL ) {
\r
1804 DeleteObject( hPieceFace[index] );
\r
1807 hPieceFace[index] = hbm;
\r
1810 static int TranslatePieceToFontPiece( int piece )
\r
1840 case BlackMarshall:
\r
1844 case BlackNightrider:
\r
1850 case BlackUnicorn:
\r
1854 case BlackGrasshopper:
\r
1866 case BlackCardinal:
\r
1873 case WhiteMarshall:
\r
1877 case WhiteNightrider:
\r
1883 case WhiteUnicorn:
\r
1887 case WhiteGrasshopper:
\r
1899 case WhiteCardinal:
\r
1908 void CreatePiecesFromFont()
\r
1911 HDC hdc_window = NULL;
\r
1917 if( fontBitmapSquareSize < 0 ) {
\r
1918 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
1922 if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1923 fontBitmapSquareSize = -1;
\r
1927 if( fontBitmapSquareSize != squareSize ) {
\r
1928 hdc_window = GetDC( hwndMain );
\r
1929 hdc = CreateCompatibleDC( hdc_window );
\r
1931 if( hPieceFont != NULL ) {
\r
1932 DeleteObject( hPieceFont );
\r
1935 for( i=0; i<=(int)BlackKing; i++ ) {
\r
1936 hPieceMask[i] = NULL;
\r
1937 hPieceFace[i] = NULL;
\r
1943 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
1944 fontHeight = appData.fontPieceSize;
\r
1947 fontHeight = (fontHeight * squareSize) / 100;
\r
1949 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
1951 lf.lfEscapement = 0;
\r
1952 lf.lfOrientation = 0;
\r
1953 lf.lfWeight = FW_NORMAL;
\r
1955 lf.lfUnderline = 0;
\r
1956 lf.lfStrikeOut = 0;
\r
1957 lf.lfCharSet = DEFAULT_CHARSET;
\r
1958 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1959 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1960 lf.lfQuality = PROOF_QUALITY;
\r
1961 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
1962 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
1963 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
1965 hPieceFont = CreateFontIndirect( &lf );
\r
1967 if( hPieceFont == NULL ) {
\r
1968 fontBitmapSquareSize = -2;
\r
1971 /* Setup font-to-piece character table */
\r
1972 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
1973 /* No (or wrong) global settings, try to detect the font */
\r
1974 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
1976 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
1978 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
1979 /* DiagramTT* family */
\r
1980 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
1982 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
1983 /* Fairy symbols */
\r
1984 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
1986 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
1987 /* Good Companion (Some characters get warped as literal :-( */
\r
1988 char s[] = "1cmWG0??S??oYI23wgQU";
\r
1989 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
1990 SetCharTable(pieceToFontChar, s);
\r
1993 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
1994 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
1998 /* Create bitmaps */
\r
1999 hfont_old = SelectObject( hdc, hPieceFont );
\r
2000 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2001 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2002 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2004 SelectObject( hdc, hfont_old );
\r
2006 fontBitmapSquareSize = squareSize;
\r
2010 if( hdc != NULL ) {
\r
2014 if( hdc_window != NULL ) {
\r
2015 ReleaseDC( hwndMain, hdc_window );
\r
2020 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2024 sprintf(name, "%s%d%s", piece, squareSize, suffix);
\r
2025 if (gameInfo.event &&
\r
2026 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2027 strcmp(name, "k80s") == 0) {
\r
2028 strcpy(name, "tim");
\r
2030 return LoadBitmap(hinst, name);
\r
2034 /* Insert a color into the program's logical palette
\r
2035 structure. This code assumes the given color is
\r
2036 the result of the RGB or PALETTERGB macro, and it
\r
2037 knows how those macros work (which is documented).
\r
2040 InsertInPalette(COLORREF color)
\r
2042 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2044 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2045 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2046 pLogPal->palNumEntries--;
\r
2050 pe->peFlags = (char) 0;
\r
2051 pe->peRed = (char) (0xFF & color);
\r
2052 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2053 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2059 InitDrawingColors()
\r
2061 if (pLogPal == NULL) {
\r
2062 /* Allocate enough memory for a logical palette with
\r
2063 * PALETTESIZE entries and set the size and version fields
\r
2064 * of the logical palette structure.
\r
2066 pLogPal = (NPLOGPALETTE)
\r
2067 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2068 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2069 pLogPal->palVersion = 0x300;
\r
2071 pLogPal->palNumEntries = 0;
\r
2073 InsertInPalette(lightSquareColor);
\r
2074 InsertInPalette(darkSquareColor);
\r
2075 InsertInPalette(whitePieceColor);
\r
2076 InsertInPalette(blackPieceColor);
\r
2077 InsertInPalette(highlightSquareColor);
\r
2078 InsertInPalette(premoveHighlightColor);
\r
2080 /* create a logical color palette according the information
\r
2081 * in the LOGPALETTE structure.
\r
2083 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2085 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2086 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2087 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2088 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2089 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2090 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2091 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2092 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
2093 /* [AS] Force rendering of the font-based pieces */
\r
2094 if( fontBitmapSquareSize > 0 ) {
\r
2095 fontBitmapSquareSize = 0;
\r
2101 BoardWidth(int boardSize, int n)
\r
2102 { /* [HGM] argument n added to allow different width and height */
\r
2103 int lineGap = sizeInfo[boardSize].lineGap;
\r
2105 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2106 lineGap = appData.overrideLineGap;
\r
2109 return (n + 1) * lineGap +
\r
2110 n * sizeInfo[boardSize].squareSize;
\r
2113 /* Respond to board resize by dragging edge */
\r
2115 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2117 BoardSize newSize = NUM_SIZES - 1;
\r
2118 static int recurse = 0;
\r
2119 if (IsIconic(hwndMain)) return;
\r
2120 if (recurse > 0) return;
\r
2122 while (newSize > 0) {
\r
2123 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2124 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2125 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2128 boardSize = newSize;
\r
2129 InitDrawingSizes(boardSize, flags);
\r
2134 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2137 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2139 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2140 ChessSquare piece;
\r
2141 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2143 SIZE clockSize, messageSize;
\r
2145 char buf[MSG_SIZ];
\r
2147 HMENU hmenu = GetMenu(hwndMain);
\r
2148 RECT crect, wrect, oldRect;
\r
2150 LOGBRUSH logbrush;
\r
2152 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2153 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2155 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2156 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2158 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2159 oldRect.top = wpMain.y;
\r
2160 oldRect.right = wpMain.x + wpMain.width;
\r
2161 oldRect.bottom = wpMain.y + wpMain.height;
\r
2163 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2164 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2165 squareSize = sizeInfo[boardSize].squareSize;
\r
2166 lineGap = sizeInfo[boardSize].lineGap;
\r
2167 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2169 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2170 lineGap = appData.overrideLineGap;
\r
2173 if (tinyLayout != oldTinyLayout) {
\r
2174 long style = GetWindowLong(hwndMain, GWL_STYLE);
\r
2176 style &= ~WS_SYSMENU;
\r
2177 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2178 "&Minimize\tCtrl+F4");
\r
2180 style |= WS_SYSMENU;
\r
2181 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2183 SetWindowLong(hwndMain, GWL_STYLE, style);
\r
2185 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2186 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2187 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2189 DrawMenuBar(hwndMain);
\r
2192 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
2193 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
2195 /* Get text area sizes */
\r
2196 hdc = GetDC(hwndMain);
\r
2197 if (appData.clockMode) {
\r
2198 sprintf(buf, _("White: %s"), TimeString(23*60*60*1000L));
\r
2200 sprintf(buf, _("White"));
\r
2202 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2203 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2204 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2205 str = _("We only care about the height here");
\r
2206 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2207 SelectObject(hdc, oldFont);
\r
2208 ReleaseDC(hwndMain, hdc);
\r
2210 /* Compute where everything goes */
\r
2211 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2212 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2213 logoHeight = 2*clockSize.cy;
\r
2214 leftLogoRect.left = OUTER_MARGIN;
\r
2215 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2216 leftLogoRect.top = OUTER_MARGIN;
\r
2217 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2219 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2220 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2221 rightLogoRect.top = OUTER_MARGIN;
\r
2222 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2225 whiteRect.left = leftLogoRect.right;
\r
2226 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2227 whiteRect.top = OUTER_MARGIN;
\r
2228 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2230 blackRect.right = rightLogoRect.left;
\r
2231 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2232 blackRect.top = whiteRect.top;
\r
2233 blackRect.bottom = whiteRect.bottom;
\r
2235 whiteRect.left = OUTER_MARGIN;
\r
2236 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2237 whiteRect.top = OUTER_MARGIN;
\r
2238 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2240 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2241 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2242 blackRect.top = whiteRect.top;
\r
2243 blackRect.bottom = whiteRect.bottom;
\r
2245 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2248 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2249 if (appData.showButtonBar) {
\r
2250 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2251 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2253 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2255 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2256 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2258 boardRect.left = OUTER_MARGIN;
\r
2259 boardRect.right = boardRect.left + boardWidth;
\r
2260 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2261 boardRect.bottom = boardRect.top + boardHeight;
\r
2263 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2264 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2265 oldBoardSize = boardSize;
\r
2266 oldTinyLayout = tinyLayout;
\r
2267 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2268 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2269 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2270 winW *= 1 + twoBoards;
\r
2271 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2272 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2273 wpMain.height = winH; // without disturbing window attachments
\r
2274 GetWindowRect(hwndMain, &wrect);
\r
2275 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2276 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2278 // [HGM] placement: let attached windows follow size change.
\r
2279 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2280 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2281 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2282 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2283 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2285 /* compensate if menu bar wrapped */
\r
2286 GetClientRect(hwndMain, &crect);
\r
2287 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2288 wpMain.height += offby;
\r
2290 case WMSZ_TOPLEFT:
\r
2291 SetWindowPos(hwndMain, NULL,
\r
2292 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2293 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2296 case WMSZ_TOPRIGHT:
\r
2298 SetWindowPos(hwndMain, NULL,
\r
2299 wrect.left, wrect.bottom - wpMain.height,
\r
2300 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2303 case WMSZ_BOTTOMLEFT:
\r
2305 SetWindowPos(hwndMain, NULL,
\r
2306 wrect.right - wpMain.width, wrect.top,
\r
2307 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2310 case WMSZ_BOTTOMRIGHT:
\r
2314 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2315 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2320 for (i = 0; i < N_BUTTONS; i++) {
\r
2321 if (buttonDesc[i].hwnd != NULL) {
\r
2322 DestroyWindow(buttonDesc[i].hwnd);
\r
2323 buttonDesc[i].hwnd = NULL;
\r
2325 if (appData.showButtonBar) {
\r
2326 buttonDesc[i].hwnd =
\r
2327 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2328 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2329 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2330 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2331 (HMENU) buttonDesc[i].id,
\r
2332 (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);
\r
2334 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2335 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2336 MAKELPARAM(FALSE, 0));
\r
2338 if (buttonDesc[i].id == IDM_Pause)
\r
2339 hwndPause = buttonDesc[i].hwnd;
\r
2340 buttonDesc[i].wndproc = (WNDPROC)
\r
2341 SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);
\r
2344 if (gridPen != NULL) DeleteObject(gridPen);
\r
2345 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2346 if (premovePen != NULL) DeleteObject(premovePen);
\r
2347 if (lineGap != 0) {
\r
2348 logbrush.lbStyle = BS_SOLID;
\r
2349 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2351 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2352 lineGap, &logbrush, 0, NULL);
\r
2353 logbrush.lbColor = highlightSquareColor;
\r
2355 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2356 lineGap, &logbrush, 0, NULL);
\r
2358 logbrush.lbColor = premoveHighlightColor;
\r
2360 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2361 lineGap, &logbrush, 0, NULL);
\r
2363 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2364 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2365 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2366 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2367 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2368 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2369 BOARD_WIDTH * (squareSize + lineGap);
\r
2370 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2372 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2373 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2374 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2375 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2376 lineGap / 2 + (i * (squareSize + lineGap));
\r
2377 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2378 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2379 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2383 /* [HGM] Licensing requirement */
\r
2385 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2388 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2390 GothicPopUp( "", VariantNormal);
\r
2393 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2395 /* Load piece bitmaps for this board size */
\r
2396 for (i=0; i<=2; i++) {
\r
2397 for (piece = WhitePawn;
\r
2398 (int) piece < (int) BlackPawn;
\r
2399 piece = (ChessSquare) ((int) piece + 1)) {
\r
2400 if (pieceBitmap[i][piece] != NULL)
\r
2401 DeleteObject(pieceBitmap[i][piece]);
\r
2405 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2406 // Orthodox Chess pieces
\r
2407 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2408 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2409 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2410 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2411 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2412 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2413 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2414 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2415 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2416 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2417 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2418 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2419 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2420 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2421 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2422 if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {
\r
2423 // in Shogi, Hijack the unused Queen for Lance
\r
2424 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2425 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2426 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2428 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2429 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2430 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2433 if(squareSize <= 72 && squareSize >= 33) {
\r
2434 /* A & C are available in most sizes now */
\r
2435 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2436 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2437 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2438 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2439 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2440 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2441 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2442 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2443 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2444 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2445 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2446 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2447 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2448 } else { // Smirf-like
\r
2449 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2450 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2451 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2453 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2454 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2455 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2456 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2457 } else { // WinBoard standard
\r
2458 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2459 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2460 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2465 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2466 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2467 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2468 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2469 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2470 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2471 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2472 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2473 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2474 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2475 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2476 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2477 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2478 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2479 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2480 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2481 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2482 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2483 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2484 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2485 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2486 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2487 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2488 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2489 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2490 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2491 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2492 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2493 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2494 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2495 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2497 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2498 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2499 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2500 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2501 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2502 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2503 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2504 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2505 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2506 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2507 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2508 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2509 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2511 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2512 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2513 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2514 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2515 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2516 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2517 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2518 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2519 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2520 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2521 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2522 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2525 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2526 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2527 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2528 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2529 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2530 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2531 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2532 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2533 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2534 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2535 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2536 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2537 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2538 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2539 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2543 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2544 /* special Shogi support in this size */
\r
2545 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2546 for (piece = WhitePawn;
\r
2547 (int) piece < (int) BlackPawn;
\r
2548 piece = (ChessSquare) ((int) piece + 1)) {
\r
2549 if (pieceBitmap[i][piece] != NULL)
\r
2550 DeleteObject(pieceBitmap[i][piece]);
\r
2553 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2554 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2555 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2556 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2557 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2558 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2559 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2560 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2561 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2562 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2563 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2564 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2565 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2566 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2567 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2568 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2569 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2570 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2571 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2572 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2573 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2574 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2575 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2576 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2577 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2578 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2579 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2580 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2581 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2582 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2583 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2584 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2585 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2586 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2587 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2588 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2589 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2590 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2591 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2592 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2593 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2594 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2600 PieceBitmap(ChessSquare p, int kind)
\r
2602 if ((int) p >= (int) BlackPawn)
\r
2603 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2605 return pieceBitmap[kind][(int) p];
\r
2608 /***************************************************************/
\r
2610 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2611 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2613 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2614 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2618 SquareToPos(int row, int column, int * x, int * y)
\r
2621 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2622 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2624 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2625 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2630 DrawCoordsOnDC(HDC hdc)
\r
2632 static char files[24] = {'0', '1','2','3','4','5','6','7','8','9','0','1','1','0','9','8','7','6','5','4','3','2','1','0'};
\r
2633 static char ranks[24] = {'l', 'k','j','i','h','g','f','e','d','c','b','a','a','b','c','d','e','f','g','h','i','j','k','l'};
\r
2634 char str[2] = { NULLCHAR, NULLCHAR };
\r
2635 int oldMode, oldAlign, x, y, start, i;
\r
2639 if (!appData.showCoords)
\r
2642 start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;
\r
2644 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2645 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2646 oldAlign = GetTextAlign(hdc);
\r
2647 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2649 y = boardRect.top + lineGap;
\r
2650 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2652 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2653 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2654 str[0] = files[start + i];
\r
2655 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2656 y += squareSize + lineGap;
\r
2659 start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;
\r
2661 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2662 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2663 str[0] = ranks[start + i];
\r
2664 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2665 x += squareSize + lineGap;
\r
2668 SelectObject(hdc, oldBrush);
\r
2669 SetBkMode(hdc, oldMode);
\r
2670 SetTextAlign(hdc, oldAlign);
\r
2671 SelectObject(hdc, oldFont);
\r
2675 DrawGridOnDC(HDC hdc)
\r
2679 if (lineGap != 0) {
\r
2680 oldPen = SelectObject(hdc, gridPen);
\r
2681 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2682 SelectObject(hdc, oldPen);
\r
2686 #define HIGHLIGHT_PEN 0
\r
2687 #define PREMOVE_PEN 1
\r
2690 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2693 HPEN oldPen, hPen;
\r
2694 if (lineGap == 0) return;
\r
2696 x1 = boardRect.left +
\r
2697 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2698 y1 = boardRect.top +
\r
2699 lineGap/2 + y * (squareSize + lineGap);
\r
2701 x1 = boardRect.left +
\r
2702 lineGap/2 + x * (squareSize + lineGap);
\r
2703 y1 = boardRect.top +
\r
2704 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2706 hPen = pen ? premovePen : highlightPen;
\r
2707 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2708 MoveToEx(hdc, x1, y1, NULL);
\r
2709 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2710 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2711 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2712 LineTo(hdc, x1, y1);
\r
2713 SelectObject(hdc, oldPen);
\r
2717 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2720 for (i=0; i<2; i++) {
\r
2721 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2722 DrawHighlightOnDC(hdc, TRUE,
\r
2723 h->sq[i].x, h->sq[i].y,
\r
2728 /* Note: sqcolor is used only in monoMode */
\r
2729 /* Note that this code is largely duplicated in woptions.c,
\r
2730 function DrawSampleSquare, so that needs to be updated too */
\r
2732 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2734 HBITMAP oldBitmap;
\r
2738 if (appData.blindfold) return;
\r
2740 /* [AS] Use font-based pieces if needed */
\r
2741 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2742 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2743 CreatePiecesFromFont();
\r
2745 if( fontBitmapSquareSize == squareSize ) {
\r
2746 int index = TranslatePieceToFontPiece(piece);
\r
2748 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2750 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2751 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2755 squareSize, squareSize,
\r
2760 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2762 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2763 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2767 squareSize, squareSize,
\r
2776 if (appData.monoMode) {
\r
2777 SelectObject(tmphdc, PieceBitmap(piece,
\r
2778 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2779 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2780 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2782 tmpSize = squareSize;
\r
2784 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2785 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2786 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2787 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2788 x += (squareSize - minorSize)>>1;
\r
2789 y += squareSize - minorSize - 2;
\r
2790 tmpSize = minorSize;
\r
2792 if (color || appData.allWhite ) {
\r
2793 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2795 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2796 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2797 if(appData.upsideDown && color==flipView)
\r
2798 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2800 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2801 /* Use black for outline of white pieces */
\r
2802 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2803 if(appData.upsideDown && color==flipView)
\r
2804 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2806 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2808 /* Use square color for details of black pieces */
\r
2809 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2810 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2811 if(appData.upsideDown && !flipView)
\r
2812 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2814 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2816 SelectObject(hdc, oldBrush);
\r
2817 SelectObject(tmphdc, oldBitmap);
\r
2821 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2822 int GetBackTextureMode( int algo )
\r
2824 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2828 case BACK_TEXTURE_MODE_PLAIN:
\r
2829 result = 1; /* Always use identity map */
\r
2831 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2832 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2840 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2841 to handle redraws cleanly (as random numbers would always be different).
\r
2843 VOID RebuildTextureSquareInfo()
\r
2853 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2855 if( liteBackTexture != NULL ) {
\r
2856 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2857 lite_w = bi.bmWidth;
\r
2858 lite_h = bi.bmHeight;
\r
2862 if( darkBackTexture != NULL ) {
\r
2863 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2864 dark_w = bi.bmWidth;
\r
2865 dark_h = bi.bmHeight;
\r
2869 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2870 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2871 if( (col + row) & 1 ) {
\r
2873 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2874 if( lite_w >= squareSize*BOARD_WIDTH )
\r
2875 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
2877 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2878 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
2879 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
2881 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2882 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2887 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2888 if( dark_w >= squareSize*BOARD_WIDTH )
\r
2889 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
2891 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2892 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
2893 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
2895 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2896 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2903 /* [AS] Arrow highlighting support */
\r
2905 static int A_WIDTH = 5; /* Width of arrow body */
\r
2907 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2908 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2910 static double Sqr( double x )
\r
2915 static int Round( double x )
\r
2917 return (int) (x + 0.5);
\r
2920 /* Draw an arrow between two points using current settings */
\r
2921 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
2924 double dx, dy, j, k, x, y;
\r
2926 if( d_x == s_x ) {
\r
2927 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2929 arrow[0].x = s_x + A_WIDTH;
\r
2932 arrow[1].x = s_x + A_WIDTH;
\r
2933 arrow[1].y = d_y - h;
\r
2935 arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;
\r
2936 arrow[2].y = d_y - h;
\r
2941 arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;
\r
2942 arrow[4].y = d_y - h;
\r
2944 arrow[5].x = s_x - A_WIDTH;
\r
2945 arrow[5].y = d_y - h;
\r
2947 arrow[6].x = s_x - A_WIDTH;
\r
2950 else if( d_y == s_y ) {
\r
2951 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2954 arrow[0].y = s_y + A_WIDTH;
\r
2956 arrow[1].x = d_x - w;
\r
2957 arrow[1].y = s_y + A_WIDTH;
\r
2959 arrow[2].x = d_x - w;
\r
2960 arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;
\r
2965 arrow[4].x = d_x - w;
\r
2966 arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;
\r
2968 arrow[5].x = d_x - w;
\r
2969 arrow[5].y = s_y - A_WIDTH;
\r
2972 arrow[6].y = s_y - A_WIDTH;
\r
2975 /* [AS] Needed a lot of paper for this! :-) */
\r
2976 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
2977 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
2979 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
2981 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
2986 arrow[0].x = Round(x - j);
\r
2987 arrow[0].y = Round(y + j*dx);
\r
2989 arrow[1].x = Round(x + j);
\r
2990 arrow[1].y = Round(y - j*dx);
\r
2993 x = (double) d_x - k;
\r
2994 y = (double) d_y - k*dy;
\r
2997 x = (double) d_x + k;
\r
2998 y = (double) d_y + k*dy;
\r
3001 arrow[2].x = Round(x + j);
\r
3002 arrow[2].y = Round(y - j*dx);
\r
3004 arrow[3].x = Round(x + j*A_WIDTH_FACTOR);
\r
3005 arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);
\r
3010 arrow[5].x = Round(x - j*A_WIDTH_FACTOR);
\r
3011 arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);
\r
3013 arrow[6].x = Round(x - j);
\r
3014 arrow[6].y = Round(y + j*dx);
\r
3017 Polygon( hdc, arrow, 7 );
\r
3020 /* [AS] Draw an arrow between two squares */
\r
3021 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3023 int s_x, s_y, d_x, d_y;
\r
3030 if( s_col == d_col && s_row == d_row ) {
\r
3034 /* Get source and destination points */
\r
3035 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3036 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3039 d_y += squareSize / 4;
\r
3041 else if( d_y < s_y ) {
\r
3042 d_y += 3 * squareSize / 4;
\r
3045 d_y += squareSize / 2;
\r
3049 d_x += squareSize / 4;
\r
3051 else if( d_x < s_x ) {
\r
3052 d_x += 3 * squareSize / 4;
\r
3055 d_x += squareSize / 2;
\r
3058 s_x += squareSize / 2;
\r
3059 s_y += squareSize / 2;
\r
3061 /* Adjust width */
\r
3062 A_WIDTH = squareSize / 14;
\r
3065 stLB.lbStyle = BS_SOLID;
\r
3066 stLB.lbColor = appData.highlightArrowColor;
\r
3069 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3070 holdpen = SelectObject( hdc, hpen );
\r
3071 hbrush = CreateBrushIndirect( &stLB );
\r
3072 holdbrush = SelectObject( hdc, hbrush );
\r
3074 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3076 SelectObject( hdc, holdpen );
\r
3077 SelectObject( hdc, holdbrush );
\r
3078 DeleteObject( hpen );
\r
3079 DeleteObject( hbrush );
\r
3082 BOOL HasHighlightInfo()
\r
3084 BOOL result = FALSE;
\r
3086 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3087 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3095 BOOL IsDrawArrowEnabled()
\r
3097 BOOL result = FALSE;
\r
3099 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3106 VOID DrawArrowHighlight( HDC hdc )
\r
3108 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3109 DrawArrowBetweenSquares( hdc,
\r
3110 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3111 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3115 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3117 HRGN result = NULL;
\r
3119 if( HasHighlightInfo() ) {
\r
3120 int x1, y1, x2, y2;
\r
3121 int sx, sy, dx, dy;
\r
3123 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3124 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3126 sx = MIN( x1, x2 );
\r
3127 sy = MIN( y1, y2 );
\r
3128 dx = MAX( x1, x2 ) + squareSize;
\r
3129 dy = MAX( y1, y2 ) + squareSize;
\r
3131 result = CreateRectRgn( sx, sy, dx, dy );
\r
3138 Warning: this function modifies the behavior of several other functions.
\r
3140 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3141 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3142 repaint is scattered all over the place, which is not good for features such as
\r
3143 "arrow highlighting" that require a full repaint of the board.
\r
3145 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3146 user interaction, when speed is not so important) but especially to avoid errors
\r
3147 in the displayed graphics.
\r
3149 In such patched places, I always try refer to this function so there is a single
\r
3150 place to maintain knowledge.
\r
3152 To restore the original behavior, just return FALSE unconditionally.
\r
3154 BOOL IsFullRepaintPreferrable()
\r
3156 BOOL result = FALSE;
\r
3158 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3159 /* Arrow may appear on the board */
\r
3167 This function is called by DrawPosition to know whether a full repaint must
\r
3170 Only DrawPosition may directly call this function, which makes use of
\r
3171 some state information. Other function should call DrawPosition specifying
\r
3172 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3174 BOOL DrawPositionNeedsFullRepaint()
\r
3176 BOOL result = FALSE;
\r
3179 Probably a slightly better policy would be to trigger a full repaint
\r
3180 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3181 but animation is fast enough that it's difficult to notice.
\r
3183 if( animInfo.piece == EmptySquare ) {
\r
3184 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3193 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3195 int row, column, x, y, square_color, piece_color;
\r
3196 ChessSquare piece;
\r
3198 HDC texture_hdc = NULL;
\r
3200 /* [AS] Initialize background textures if needed */
\r
3201 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3202 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3203 if( backTextureSquareSize != squareSize
\r
3204 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3205 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3206 backTextureSquareSize = squareSize;
\r
3207 RebuildTextureSquareInfo();
\r
3210 texture_hdc = CreateCompatibleDC( hdc );
\r
3213 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3214 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3216 SquareToPos(row, column, &x, &y);
\r
3218 piece = board[row][column];
\r
3220 square_color = ((column + row) % 2) == 1;
\r
3221 if( gameInfo.variant == VariantXiangqi ) {
\r
3222 square_color = !InPalace(row, column);
\r
3223 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3224 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3226 piece_color = (int) piece < (int) BlackPawn;
\r
3229 /* [HGM] holdings file: light square or black */
\r
3230 if(column == BOARD_LEFT-2) {
\r
3231 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3234 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3238 if(column == BOARD_RGHT + 1 ) {
\r
3239 if( row < gameInfo.holdingsSize )
\r
3242 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3246 if(column == BOARD_LEFT-1 ) /* left align */
\r
3247 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3248 else if( column == BOARD_RGHT) /* right align */
\r
3249 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3251 if (appData.monoMode) {
\r
3252 if (piece == EmptySquare) {
\r
3253 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3254 square_color ? WHITENESS : BLACKNESS);
\r
3256 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3259 else if( backTextureSquareInfo[row][column].mode > 0 ) {
\r
3260 /* [AS] Draw the square using a texture bitmap */
\r
3261 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3262 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3263 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3266 squareSize, squareSize,
\r
3269 backTextureSquareInfo[r][c].mode,
\r
3270 backTextureSquareInfo[r][c].x,
\r
3271 backTextureSquareInfo[r][c].y );
\r
3273 SelectObject( texture_hdc, hbm );
\r
3275 if (piece != EmptySquare) {
\r
3276 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3280 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3282 oldBrush = SelectObject(hdc, brush );
\r
3283 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3284 SelectObject(hdc, oldBrush);
\r
3285 if (piece != EmptySquare)
\r
3286 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3291 if( texture_hdc != NULL ) {
\r
3292 DeleteDC( texture_hdc );
\r
3296 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3297 void fputDW(FILE *f, int x)
\r
3299 fputc(x & 255, f);
\r
3300 fputc(x>>8 & 255, f);
\r
3301 fputc(x>>16 & 255, f);
\r
3302 fputc(x>>24 & 255, f);
\r
3305 #define MAX_CLIPS 200 /* more than enough */
\r
3308 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3310 // HBITMAP bufferBitmap;
\r
3315 int w = 100, h = 50;
\r
3317 if(logo == NULL) return;
\r
3318 // GetClientRect(hwndMain, &Rect);
\r
3319 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3320 // Rect.bottom-Rect.top+1);
\r
3321 tmphdc = CreateCompatibleDC(hdc);
\r
3322 hbm = SelectObject(tmphdc, logo);
\r
3323 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3327 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3328 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3329 SelectObject(tmphdc, hbm);
\r
3333 static HDC hdcSeek;
\r
3335 // [HGM] seekgraph
\r
3336 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3339 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3340 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3341 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3342 SelectObject( hdcSeek, hp );
\r
3345 // front-end wrapper for drawing functions to do rectangles
\r
3346 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3351 if (hdcSeek == NULL) {
\r
3352 hdcSeek = GetDC(hwndMain);
\r
3353 if (!appData.monoMode) {
\r
3354 SelectPalette(hdcSeek, hPal, FALSE);
\r
3355 RealizePalette(hdcSeek);
\r
3358 hp = SelectObject( hdcSeek, gridPen );
\r
3359 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3360 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3361 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3362 SelectObject( hdcSeek, hp );
\r
3365 // front-end wrapper for putting text in graph
\r
3366 void DrawSeekText(char *buf, int x, int y)
\r
3369 SetBkMode( hdcSeek, TRANSPARENT );
\r
3370 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3371 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3374 void DrawSeekDot(int x, int y, int color)
\r
3376 int square = color & 0x80;
\r
3377 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3378 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3381 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3382 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3384 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3385 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3386 SelectObject(hdcSeek, oldBrush);
\r
3390 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3392 static Board lastReq[2], lastDrawn[2];
\r
3393 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3394 static int lastDrawnFlipView = 0;
\r
3395 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3396 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3399 HBITMAP bufferBitmap;
\r
3400 HBITMAP oldBitmap;
\r
3402 HRGN clips[MAX_CLIPS];
\r
3403 ChessSquare dragged_piece = EmptySquare;
\r
3404 int nr = twoBoards*partnerUp;
\r
3406 /* I'm undecided on this - this function figures out whether a full
\r
3407 * repaint is necessary on its own, so there's no real reason to have the
\r
3408 * caller tell it that. I think this can safely be set to FALSE - but
\r
3409 * if we trust the callers not to request full repaints unnessesarily, then
\r
3410 * we could skip some clipping work. In other words, only request a full
\r
3411 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3412 * gamestart and similar) --Hawk
\r
3414 Boolean fullrepaint = repaint;
\r
3416 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3418 if( DrawPositionNeedsFullRepaint() ) {
\r
3419 fullrepaint = TRUE;
\r
3422 if (board == NULL) {
\r
3423 if (!lastReqValid[nr]) {
\r
3426 board = lastReq[nr];
\r
3428 CopyBoard(lastReq[nr], board);
\r
3429 lastReqValid[nr] = 1;
\r
3432 if (doingSizing) {
\r
3436 if (IsIconic(hwndMain)) {
\r
3440 if (hdc == NULL) {
\r
3441 hdc = GetDC(hwndMain);
\r
3442 if (!appData.monoMode) {
\r
3443 SelectPalette(hdc, hPal, FALSE);
\r
3444 RealizePalette(hdc);
\r
3448 releaseDC = FALSE;
\r
3451 /* Create some work-DCs */
\r
3452 hdcmem = CreateCompatibleDC(hdc);
\r
3453 tmphdc = CreateCompatibleDC(hdc);
\r
3455 /* If dragging is in progress, we temporarely remove the piece */
\r
3456 /* [HGM] or temporarily decrease count if stacked */
\r
3457 /* !! Moved to before board compare !! */
\r
3458 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3459 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3460 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3461 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3462 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3464 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3465 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3466 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3468 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3471 /* Figure out which squares need updating by comparing the
\r
3472 * newest board with the last drawn board and checking if
\r
3473 * flipping has changed.
\r
3475 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3476 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3477 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3478 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3479 SquareToPos(row, column, &x, &y);
\r
3480 clips[num_clips++] =
\r
3481 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3485 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3486 for (i=0; i<2; i++) {
\r
3487 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3488 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3489 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3490 lastDrawnHighlight.sq[i].y >= 0) {
\r
3491 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3492 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3493 clips[num_clips++] =
\r
3494 CreateRectRgn(x - lineGap, y - lineGap,
\r
3495 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3497 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3498 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3499 clips[num_clips++] =
\r
3500 CreateRectRgn(x - lineGap, y - lineGap,
\r
3501 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3505 for (i=0; i<2; i++) {
\r
3506 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3507 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3508 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3509 lastDrawnPremove.sq[i].y >= 0) {
\r
3510 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3511 lastDrawnPremove.sq[i].x, &x, &y);
\r
3512 clips[num_clips++] =
\r
3513 CreateRectRgn(x - lineGap, y - lineGap,
\r
3514 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3516 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3517 premoveHighlightInfo.sq[i].y >= 0) {
\r
3518 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3519 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3520 clips[num_clips++] =
\r
3521 CreateRectRgn(x - lineGap, y - lineGap,
\r
3522 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3526 } else { // nr == 1
\r
3527 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3528 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3529 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3530 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3531 for (i=0; i<2; i++) {
\r
3532 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3533 partnerHighlightInfo.sq[i].y >= 0) {
\r
3534 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3535 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3536 clips[num_clips++] =
\r
3537 CreateRectRgn(x - lineGap, y - lineGap,
\r
3538 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3540 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3541 oldPartnerHighlight.sq[i].y >= 0) {
\r
3542 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3543 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3544 clips[num_clips++] =
\r
3545 CreateRectRgn(x - lineGap, y - lineGap,
\r
3546 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3551 fullrepaint = TRUE;
\r
3554 /* Create a buffer bitmap - this is the actual bitmap
\r
3555 * being written to. When all the work is done, we can
\r
3556 * copy it to the real DC (the screen). This avoids
\r
3557 * the problems with flickering.
\r
3559 GetClientRect(hwndMain, &Rect);
\r
3560 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3561 Rect.bottom-Rect.top+1);
\r
3562 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3563 if (!appData.monoMode) {
\r
3564 SelectPalette(hdcmem, hPal, FALSE);
\r
3567 /* Create clips for dragging */
\r
3568 if (!fullrepaint) {
\r
3569 if (dragInfo.from.x >= 0) {
\r
3570 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3571 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3573 if (dragInfo.start.x >= 0) {
\r
3574 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3575 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3577 if (dragInfo.pos.x >= 0) {
\r
3578 x = dragInfo.pos.x - squareSize / 2;
\r
3579 y = dragInfo.pos.y - squareSize / 2;
\r
3580 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3582 if (dragInfo.lastpos.x >= 0) {
\r
3583 x = dragInfo.lastpos.x - squareSize / 2;
\r
3584 y = dragInfo.lastpos.y - squareSize / 2;
\r
3585 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3589 /* Are we animating a move?
\r
3591 * - remove the piece from the board (temporarely)
\r
3592 * - calculate the clipping region
\r
3594 if (!fullrepaint) {
\r
3595 if (animInfo.piece != EmptySquare) {
\r
3596 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3597 x = boardRect.left + animInfo.lastpos.x;
\r
3598 y = boardRect.top + animInfo.lastpos.y;
\r
3599 x2 = boardRect.left + animInfo.pos.x;
\r
3600 y2 = boardRect.top + animInfo.pos.y;
\r
3601 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3602 /* Slight kludge. The real problem is that after AnimateMove is
\r
3603 done, the position on the screen does not match lastDrawn.
\r
3604 This currently causes trouble only on e.p. captures in
\r
3605 atomic, where the piece moves to an empty square and then
\r
3606 explodes. The old and new positions both had an empty square
\r
3607 at the destination, but animation has drawn a piece there and
\r
3608 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3609 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3613 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3614 if (num_clips == 0)
\r
3615 fullrepaint = TRUE;
\r
3617 /* Set clipping on the memory DC */
\r
3618 if (!fullrepaint) {
\r
3619 SelectClipRgn(hdcmem, clips[0]);
\r
3620 for (x = 1; x < num_clips; x++) {
\r
3621 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3622 abort(); // this should never ever happen!
\r
3626 /* Do all the drawing to the memory DC */
\r
3627 if(explodeInfo.radius) { // [HGM] atomic
\r
3629 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3630 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3631 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3632 x += squareSize/2;
\r
3633 y += squareSize/2;
\r
3634 if(!fullrepaint) {
\r
3635 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3636 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3638 DrawGridOnDC(hdcmem);
\r
3639 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3640 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3641 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3642 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3643 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3644 SelectObject(hdcmem, oldBrush);
\r
3646 DrawGridOnDC(hdcmem);
\r
3647 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3648 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3649 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3651 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3652 oldPartnerHighlight = partnerHighlightInfo;
\r
3654 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3656 if(nr == 0) // [HGM] dual: markers only on left board
\r
3657 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3658 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3659 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3660 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3661 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3662 SquareToPos(row, column, &x, &y);
\r
3663 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3664 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3665 SelectObject(hdcmem, oldBrush);
\r
3670 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3671 if(appData.autoLogo) {
\r
3673 switch(gameMode) { // pick logos based on game mode
\r
3674 case IcsObserving:
\r
3675 whiteLogo = second.programLogo; // ICS logo
\r
3676 blackLogo = second.programLogo;
\r
3679 case IcsPlayingWhite:
\r
3680 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3681 blackLogo = second.programLogo; // ICS logo
\r
3683 case IcsPlayingBlack:
\r
3684 whiteLogo = second.programLogo; // ICS logo
\r
3685 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3687 case TwoMachinesPlay:
\r
3688 if(first.twoMachinesColor[0] == 'b') {
\r
3689 whiteLogo = second.programLogo;
\r
3690 blackLogo = first.programLogo;
\r
3693 case MachinePlaysWhite:
\r
3694 blackLogo = userLogo;
\r
3696 case MachinePlaysBlack:
\r
3697 whiteLogo = userLogo;
\r
3698 blackLogo = first.programLogo;
\r
3701 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3702 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3705 if( appData.highlightMoveWithArrow ) {
\r
3706 DrawArrowHighlight(hdcmem);
\r
3709 DrawCoordsOnDC(hdcmem);
\r
3711 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3712 /* to make sure lastDrawn contains what is actually drawn */
\r
3714 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3715 if (dragged_piece != EmptySquare) {
\r
3716 /* [HGM] or restack */
\r
3717 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3718 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3720 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3721 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3722 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3723 x = dragInfo.pos.x - squareSize / 2;
\r
3724 y = dragInfo.pos.y - squareSize / 2;
\r
3725 DrawPieceOnDC(hdcmem, dragged_piece,
\r
3726 ((int) dragged_piece < (int) BlackPawn),
\r
3727 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3730 /* Put the animated piece back into place and draw it */
\r
3731 if (animInfo.piece != EmptySquare) {
\r
3732 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3733 x = boardRect.left + animInfo.pos.x;
\r
3734 y = boardRect.top + animInfo.pos.y;
\r
3735 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3736 ((int) animInfo.piece < (int) BlackPawn),
\r
3737 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3740 /* Release the bufferBitmap by selecting in the old bitmap
\r
3741 * and delete the memory DC
\r
3743 SelectObject(hdcmem, oldBitmap);
\r
3746 /* Set clipping on the target DC */
\r
3747 if (!fullrepaint) {
\r
3748 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3750 GetRgnBox(clips[x], &rect);
\r
3751 DeleteObject(clips[x]);
\r
3752 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3753 rect.right + wpMain.width/2, rect.bottom);
\r
3755 SelectClipRgn(hdc, clips[0]);
\r
3756 for (x = 1; x < num_clips; x++) {
\r
3757 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3758 abort(); // this should never ever happen!
\r
3762 /* Copy the new bitmap onto the screen in one go.
\r
3763 * This way we avoid any flickering
\r
3765 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3766 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3767 boardRect.right - boardRect.left,
\r
3768 boardRect.bottom - boardRect.top,
\r
3769 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3770 if(saveDiagFlag) {
\r
3771 BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000];
\r
3772 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3774 GetObject(bufferBitmap, sizeof(b), &b);
\r
3775 if(b.bmWidthBytes*b.bmHeight <= 990000) {
\r
3776 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3777 bih.biWidth = b.bmWidth;
\r
3778 bih.biHeight = b.bmHeight;
\r
3780 bih.biBitCount = b.bmBitsPixel;
\r
3781 bih.biCompression = 0;
\r
3782 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3783 bih.biXPelsPerMeter = 0;
\r
3784 bih.biYPelsPerMeter = 0;
\r
3785 bih.biClrUsed = 0;
\r
3786 bih.biClrImportant = 0;
\r
3787 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3788 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3789 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3790 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3792 wb = b.bmWidthBytes;
\r
3794 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3795 int k = ((int*) pData)[i];
\r
3796 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3797 if(j >= 16) break;
\r
3799 if(j >= nrColors) nrColors = j+1;
\r
3801 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3803 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3804 for(w=0; w<(wb>>2); w+=2) {
\r
3805 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3806 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3807 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3808 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3809 pData[p++] = m | j<<4;
\r
3811 while(p&3) pData[p++] = 0;
\r
3814 wb = ((wb+31)>>5)<<2;
\r
3816 // write BITMAPFILEHEADER
\r
3817 fprintf(diagFile, "BM");
\r
3818 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3819 fputDW(diagFile, 0);
\r
3820 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3821 // write BITMAPINFOHEADER
\r
3822 fputDW(diagFile, 40);
\r
3823 fputDW(diagFile, b.bmWidth);
\r
3824 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3825 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3826 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3827 fputDW(diagFile, 0);
\r
3828 fputDW(diagFile, 0);
\r
3829 fputDW(diagFile, 0);
\r
3830 fputDW(diagFile, 0);
\r
3831 fputDW(diagFile, 0);
\r
3832 fputDW(diagFile, 0);
\r
3833 // write color table
\r
3835 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3836 // write bitmap data
\r
3837 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3838 fputc(pData[i], diagFile);
\r
3842 SelectObject(tmphdc, oldBitmap);
\r
3844 /* Massive cleanup */
\r
3845 for (x = 0; x < num_clips; x++)
\r
3846 DeleteObject(clips[x]);
\r
3849 DeleteObject(bufferBitmap);
\r
3852 ReleaseDC(hwndMain, hdc);
\r
3854 if (lastDrawnFlipView != flipView && nr == 0) {
\r
3856 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3858 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3861 /* CopyBoard(lastDrawn, board);*/
\r
3862 lastDrawnHighlight = highlightInfo;
\r
3863 lastDrawnPremove = premoveHighlightInfo;
\r
3864 lastDrawnFlipView = flipView;
\r
3865 lastDrawnValid[nr] = 1;
\r
3868 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3873 saveDiagFlag = 1; diagFile = f;
\r
3874 HDCDrawPosition(NULL, TRUE, NULL);
\r
3878 // if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");
\r
3885 /*---------------------------------------------------------------------------*\
\r
3886 | CLIENT PAINT PROCEDURE
\r
3887 | This is the main event-handler for the WM_PAINT message.
\r
3889 \*---------------------------------------------------------------------------*/
\r
3891 PaintProc(HWND hwnd)
\r
3897 if((hdc = BeginPaint(hwnd, &ps))) {
\r
3898 if (IsIconic(hwnd)) {
\r
3899 DrawIcon(hdc, 2, 2, iconCurrent);
\r
3901 if (!appData.monoMode) {
\r
3902 SelectPalette(hdc, hPal, FALSE);
\r
3903 RealizePalette(hdc);
\r
3905 HDCDrawPosition(hdc, 1, NULL);
\r
3906 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
3907 flipView = !flipView; partnerUp = !partnerUp;
\r
3908 HDCDrawPosition(hdc, 1, NULL);
\r
3909 flipView = !flipView; partnerUp = !partnerUp;
\r
3912 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
3913 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
3914 ETO_CLIPPED|ETO_OPAQUE,
\r
3915 &messageRect, messageText, strlen(messageText), NULL);
\r
3916 SelectObject(hdc, oldFont);
\r
3917 DisplayBothClocks();
\r
3919 EndPaint(hwnd,&ps);
\r
3927 * If the user selects on a border boundary, return -1; if off the board,
\r
3928 * return -2. Otherwise map the event coordinate to the square.
\r
3929 * The offset boardRect.left or boardRect.top must already have been
\r
3930 * subtracted from x.
\r
3932 int EventToSquare(x, limit)
\r
3940 if ((x % (squareSize + lineGap)) >= squareSize)
\r
3942 x /= (squareSize + lineGap);
\r
3954 DropEnable dropEnables[] = {
\r
3955 { 'P', DP_Pawn, N_("Pawn") },
\r
3956 { 'N', DP_Knight, N_("Knight") },
\r
3957 { 'B', DP_Bishop, N_("Bishop") },
\r
3958 { 'R', DP_Rook, N_("Rook") },
\r
3959 { 'Q', DP_Queen, N_("Queen") },
\r
3963 SetupDropMenu(HMENU hmenu)
\r
3965 int i, count, enable;
\r
3967 extern char white_holding[], black_holding[];
\r
3968 char item[MSG_SIZ];
\r
3970 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
3971 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
3972 dropEnables[i].piece);
\r
3974 while (p && *p++ == dropEnables[i].piece) count++;
\r
3975 sprintf(item, "%s %d", T_(dropEnables[i].name), count);
\r
3976 enable = count > 0 || !appData.testLegality
\r
3977 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
3978 && !appData.icsActive);
\r
3979 ModifyMenu(hmenu, dropEnables[i].command,
\r
3980 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
3981 dropEnables[i].command, item);
\r
3985 void DragPieceBegin(int x, int y)
\r
3987 dragInfo.lastpos.x = boardRect.left + x;
\r
3988 dragInfo.lastpos.y = boardRect.top + y;
\r
3989 dragInfo.from.x = fromX;
\r
3990 dragInfo.from.y = fromY;
\r
3991 dragInfo.start = dragInfo.from;
\r
3992 SetCapture(hwndMain);
\r
3995 void DragPieceEnd(int x, int y)
\r
3998 dragInfo.start.x = dragInfo.start.y = -1;
\r
3999 dragInfo.from = dragInfo.start;
\r
4000 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4003 /* Event handler for mouse messages */
\r
4005 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4009 static int recursive = 0;
\r
4011 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4014 if (message == WM_MBUTTONUP) {
\r
4015 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4016 to the middle button: we simulate pressing the left button too!
\r
4018 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4019 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4025 pt.x = LOWORD(lParam);
\r
4026 pt.y = HIWORD(lParam);
\r
4027 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4028 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4029 if (!flipView && y >= 0) {
\r
4030 y = BOARD_HEIGHT - 1 - y;
\r
4032 if (flipView && x >= 0) {
\r
4033 x = BOARD_WIDTH - 1 - x;
\r
4036 switch (message) {
\r
4037 case WM_LBUTTONDOWN:
\r
4038 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4039 if (gameMode == EditPosition) {
\r
4040 SetWhiteToPlayEvent();
\r
4041 } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {
\r
4042 AdjustClock(flipClock, -1);
\r
4043 } else if (gameMode == IcsPlayingBlack ||
\r
4044 gameMode == MachinePlaysWhite) {
\r
4047 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4048 if (gameMode == EditPosition) {
\r
4049 SetBlackToPlayEvent();
\r
4050 } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {
\r
4051 AdjustClock(!flipClock, -1);
\r
4052 } else if (gameMode == IcsPlayingWhite ||
\r
4053 gameMode == MachinePlaysBlack) {
\r
4057 dragInfo.start.x = dragInfo.start.y = -1;
\r
4058 dragInfo.from = dragInfo.start;
\r
4059 if(fromX == -1 && frozen) { // not sure where this is for
\r
4060 fromX = fromY = -1;
\r
4061 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4064 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4065 DrawPosition(TRUE, NULL);
\r
4068 case WM_LBUTTONUP:
\r
4069 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4070 DrawPosition(TRUE, NULL);
\r
4073 case WM_MOUSEMOVE:
\r
4074 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4075 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4076 if ((appData.animateDragging || appData.highlightDragging)
\r
4077 && (wParam & MK_LBUTTON)
\r
4078 && dragInfo.from.x >= 0)
\r
4080 BOOL full_repaint = FALSE;
\r
4082 if (appData.animateDragging) {
\r
4083 dragInfo.pos = pt;
\r
4085 if (appData.highlightDragging) {
\r
4086 SetHighlights(fromX, fromY, x, y);
\r
4087 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4088 full_repaint = TRUE;
\r
4092 DrawPosition( full_repaint, NULL);
\r
4094 dragInfo.lastpos = dragInfo.pos;
\r
4098 case WM_MOUSEWHEEL: // [DM]
\r
4099 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4100 /* Mouse Wheel is being rolled forward
\r
4101 * Play moves forward
\r
4103 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4104 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4105 /* Mouse Wheel is being rolled backward
\r
4106 * Play moves backward
\r
4108 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4109 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4113 case WM_MBUTTONUP:
\r
4114 case WM_RBUTTONUP:
\r
4116 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4119 case WM_MBUTTONDOWN:
\r
4120 case WM_RBUTTONDOWN:
\r
4123 fromX = fromY = -1;
\r
4124 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4125 dragInfo.start.x = dragInfo.start.y = -1;
\r
4126 dragInfo.from = dragInfo.start;
\r
4127 dragInfo.lastpos = dragInfo.pos;
\r
4128 if (appData.highlightDragging) {
\r
4129 ClearHighlights();
\r
4132 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4133 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4134 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4135 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4136 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4140 DrawPosition(TRUE, NULL);
\r
4142 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4145 if (message == WM_MBUTTONDOWN) {
\r
4146 buttonCount = 3; /* even if system didn't think so */
\r
4147 if (wParam & MK_SHIFT)
\r
4148 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4150 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4151 } else { /* message == WM_RBUTTONDOWN */
\r
4152 /* Just have one menu, on the right button. Windows users don't
\r
4153 think to try the middle one, and sometimes other software steals
\r
4154 it, or it doesn't really exist. */
\r
4155 if(gameInfo.variant != VariantShogi)
\r
4156 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4158 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4162 SetCapture(hwndMain);
4165 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4166 SetupDropMenu(hmenu);
\r
4167 MenuPopup(hwnd, pt, hmenu, -1);
\r
4177 /* Preprocess messages for buttons in main window */
\r
4179 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4181 int id = GetWindowLong(hwnd, GWL_ID);
\r
4184 for (i=0; i<N_BUTTONS; i++) {
\r
4185 if (buttonDesc[i].id == id) break;
\r
4187 if (i == N_BUTTONS) return 0;
\r
4188 switch (message) {
\r
4193 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4194 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4201 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4204 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4205 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4206 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4207 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4209 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4211 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4212 PopUpMoveDialog((char)wParam);
\r
4218 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4221 /* Process messages for Promotion dialog box */
\r
4223 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4227 switch (message) {
\r
4228 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4229 /* Center the dialog over the application window */
\r
4230 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4231 Translate(hDlg, DLG_PromotionKing);
\r
4232 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4233 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4234 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4235 SW_SHOW : SW_HIDE);
\r
4236 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4237 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4238 ((PieceToChar(WhiteAngel) >= 'A' &&
\r
4239 PieceToChar(WhiteAngel) != '~') ||
\r
4240 (PieceToChar(BlackAngel) >= 'A' &&
\r
4241 PieceToChar(BlackAngel) != '~') ) ?
\r
4242 SW_SHOW : SW_HIDE);
\r
4243 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4244 ((PieceToChar(WhiteMarshall) >= 'A' &&
\r
4245 PieceToChar(WhiteMarshall) != '~') ||
\r
4246 (PieceToChar(BlackMarshall) >= 'A' &&
\r
4247 PieceToChar(BlackMarshall) != '~') ) ?
\r
4248 SW_SHOW : SW_HIDE);
\r
4249 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4250 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4251 gameInfo.variant != VariantShogi ?
\r
4252 SW_SHOW : SW_HIDE);
\r
4253 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4254 gameInfo.variant != VariantShogi ?
\r
4255 SW_SHOW : SW_HIDE);
\r
4256 ShowWindow(GetDlgItem(hDlg, IDC_Yes),
\r
4257 gameInfo.variant == VariantShogi ?
\r
4258 SW_SHOW : SW_HIDE);
\r
4259 ShowWindow(GetDlgItem(hDlg, IDC_No),
\r
4260 gameInfo.variant == VariantShogi ?
\r
4261 SW_SHOW : SW_HIDE);
\r
4262 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4263 gameInfo.variant == VariantSuper ?
\r
4264 SW_SHOW : SW_HIDE);
\r
4267 case WM_COMMAND: /* message: received a command */
\r
4268 switch (LOWORD(wParam)) {
\r
4270 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4271 ClearHighlights();
\r
4272 DrawPosition(FALSE, NULL);
\r
4275 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4278 promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);
\r
4281 promoChar = PieceToChar(BlackRook);
\r
4284 promoChar = PieceToChar(BlackBishop);
\r
4286 case PB_Chancellor:
\r
4287 promoChar = PieceToChar(BlackMarshall);
\r
4289 case PB_Archbishop:
\r
4290 promoChar = PieceToChar(BlackAngel);
\r
4293 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);
\r
4298 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4299 /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we
\r
4300 only show the popup when we are already sure the move is valid or
\r
4301 legal. We pass a faulty move type, but the kludge is that FinishMove
\r
4302 will figure out it is a promotion from the promoChar. */
\r
4303 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4304 fromX = fromY = -1;
\r
4305 if (!appData.highlightLastMove) {
\r
4306 ClearHighlights();
\r
4307 DrawPosition(FALSE, NULL);
\r
4314 /* Pop up promotion dialog */
\r
4316 PromotionPopup(HWND hwnd)
\r
4320 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4321 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4322 hwnd, (DLGPROC)lpProc);
\r
4323 FreeProcInstance(lpProc);
\r
4329 DrawPosition(TRUE, NULL);
\r
4330 PromotionPopup(hwndMain);
\r
4333 /* Toggle ShowThinking */
\r
4335 ToggleShowThinking()
\r
4337 appData.showThinking = !appData.showThinking;
\r
4338 ShowThinkingEvent();
\r
4342 LoadGameDialog(HWND hwnd, char* title)
\r
4346 char fileTitle[MSG_SIZ];
\r
4347 f = OpenFileDialog(hwnd, "rb", "",
\r
4348 appData.oldSaveStyle ? "gam" : "pgn",
\r
4350 title, &number, fileTitle, NULL);
\r
4352 cmailMsgLoaded = FALSE;
\r
4353 if (number == 0) {
\r
4354 int error = GameListBuild(f);
\r
4356 DisplayError(_("Cannot build game list"), error);
\r
4357 } else if (!ListEmpty(&gameList) &&
\r
4358 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4359 GameListPopUp(f, fileTitle);
\r
4362 GameListDestroy();
\r
4365 LoadGame(f, number, fileTitle, FALSE);
\r
4369 int get_term_width()
\r
4374 HFONT hfont, hold_font;
\r
4379 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4383 // get the text metrics
\r
4384 hdc = GetDC(hText);
\r
4385 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4386 if (consoleCF.dwEffects & CFE_BOLD)
\r
4387 lf.lfWeight = FW_BOLD;
\r
4388 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4389 lf.lfItalic = TRUE;
\r
4390 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4391 lf.lfStrikeOut = TRUE;
\r
4392 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4393 lf.lfUnderline = TRUE;
\r
4394 hfont = CreateFontIndirect(&lf);
\r
4395 hold_font = SelectObject(hdc, hfont);
\r
4396 GetTextMetrics(hdc, &tm);
\r
4397 SelectObject(hdc, hold_font);
\r
4398 DeleteObject(hfont);
\r
4399 ReleaseDC(hText, hdc);
\r
4401 // get the rectangle
\r
4402 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4404 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4407 void UpdateICSWidth(HWND hText)
\r
4409 LONG old_width, new_width;
\r
4411 new_width = get_term_width(hText, FALSE);
\r
4412 old_width = GetWindowLong(hText, GWL_USERDATA);
\r
4413 if (new_width != old_width)
\r
4415 ics_update_width(new_width);
\r
4416 SetWindowLong(hText, GWL_USERDATA, new_width);
\r
4421 ChangedConsoleFont()
\r
4424 CHARRANGE tmpsel, sel;
\r
4425 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4426 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4427 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4430 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4431 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4432 strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);
\r
4433 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4434 * size. This was undocumented in the version of MSVC++ that I had
\r
4435 * when I wrote the code, but is apparently documented now.
\r
4437 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4438 cfmt.bCharSet = f->lf.lfCharSet;
\r
4439 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4440 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4441 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4442 /* Why are the following seemingly needed too? */
\r
4443 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4444 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4445 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4447 tmpsel.cpMax = -1; /*999999?*/
\r
4448 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4449 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4450 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4451 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4453 paraf.cbSize = sizeof(paraf);
\r
4454 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4455 paraf.dxStartIndent = 0;
\r
4456 paraf.dxOffset = WRAP_INDENT;
\r
4457 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4458 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4459 UpdateICSWidth(hText);
\r
4462 /*---------------------------------------------------------------------------*\
\r
4464 * Window Proc for main window
\r
4466 \*---------------------------------------------------------------------------*/
\r
4468 /* Process messages for main window, etc. */
\r
4470 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4473 int wmId, wmEvent;
\r
4477 char fileTitle[MSG_SIZ];
\r
4478 char buf[MSG_SIZ];
\r
4479 static SnapData sd;
\r
4481 switch (message) {
\r
4483 case WM_PAINT: /* message: repaint portion of window */
\r
4487 case WM_ERASEBKGND:
\r
4488 if (IsIconic(hwnd)) {
\r
4489 /* Cheat; change the message */
\r
4490 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4492 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4496 case WM_LBUTTONDOWN:
\r
4497 case WM_MBUTTONDOWN:
\r
4498 case WM_RBUTTONDOWN:
\r
4499 case WM_LBUTTONUP:
\r
4500 case WM_MBUTTONUP:
\r
4501 case WM_RBUTTONUP:
\r
4502 case WM_MOUSEMOVE:
\r
4503 case WM_MOUSEWHEEL:
\r
4504 MouseEvent(hwnd, message, wParam, lParam);
\r
4507 JAWS_KB_NAVIGATION
\r
4511 JAWS_ALT_INTERCEPT
\r
4513 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4514 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4515 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4516 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4518 SendMessage(h, message, wParam, lParam);
\r
4519 } else if(lParam != KF_REPEAT) {
\r
4520 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4521 PopUpMoveDialog((char)wParam);
\r
4522 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4523 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4528 case WM_PALETTECHANGED:
\r
4529 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4531 HDC hdc = GetDC(hwndMain);
\r
4532 SelectPalette(hdc, hPal, TRUE);
\r
4533 nnew = RealizePalette(hdc);
\r
4535 paletteChanged = TRUE;
\r
4536 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4538 ReleaseDC(hwnd, hdc);
\r
4542 case WM_QUERYNEWPALETTE:
\r
4543 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4545 HDC hdc = GetDC(hwndMain);
\r
4546 paletteChanged = FALSE;
\r
4547 SelectPalette(hdc, hPal, FALSE);
\r
4548 nnew = RealizePalette(hdc);
\r
4550 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4552 ReleaseDC(hwnd, hdc);
\r
4557 case WM_COMMAND: /* message: command from application menu */
\r
4558 wmId = LOWORD(wParam);
\r
4559 wmEvent = HIWORD(wParam);
\r
4564 SAY("new game enter a move to play against the computer with white");
\r
4567 case IDM_NewGameFRC:
\r
4568 if( NewGameFRC() == 0 ) {
\r
4573 case IDM_NewVariant:
\r
4574 NewVariantPopup(hwnd);
\r
4577 case IDM_LoadGame:
\r
4578 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4581 case IDM_LoadNextGame:
\r
4585 case IDM_LoadPrevGame:
\r
4589 case IDM_ReloadGame:
\r
4593 case IDM_LoadPosition:
\r
4594 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4595 Reset(FALSE, TRUE);
\r
4598 f = OpenFileDialog(hwnd, "rb", "",
\r
4599 appData.oldSaveStyle ? "pos" : "fen",
\r
4601 _("Load Position from File"), &number, fileTitle, NULL);
\r
4603 LoadPosition(f, number, fileTitle);
\r
4607 case IDM_LoadNextPosition:
\r
4608 ReloadPosition(1);
\r
4611 case IDM_LoadPrevPosition:
\r
4612 ReloadPosition(-1);
\r
4615 case IDM_ReloadPosition:
\r
4616 ReloadPosition(0);
\r
4619 case IDM_SaveGame:
\r
4620 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4621 f = OpenFileDialog(hwnd, "a", defName,
\r
4622 appData.oldSaveStyle ? "gam" : "pgn",
\r
4624 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4626 SaveGame(f, 0, "");
\r
4630 case IDM_SavePosition:
\r
4631 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4632 f = OpenFileDialog(hwnd, "a", defName,
\r
4633 appData.oldSaveStyle ? "pos" : "fen",
\r
4635 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4637 SavePosition(f, 0, "");
\r
4641 case IDM_SaveDiagram:
\r
4642 defName = "diagram";
\r
4643 f = OpenFileDialog(hwnd, "wb", defName,
\r
4646 "Save Diagram to File", NULL, fileTitle, NULL);
\r
4652 case IDM_CopyGame:
\r
4653 CopyGameToClipboard();
\r
4656 case IDM_PasteGame:
\r
4657 PasteGameFromClipboard();
\r
4660 case IDM_CopyGameListToClipboard:
\r
4661 CopyGameListToClipboard();
\r
4664 /* [AS] Autodetect FEN or PGN data */
\r
4665 case IDM_PasteAny:
\r
4666 PasteGameOrFENFromClipboard();
\r
4669 /* [AS] Move history */
\r
4670 case IDM_ShowMoveHistory:
\r
4671 if( MoveHistoryIsUp() ) {
\r
4672 MoveHistoryPopDown();
\r
4675 MoveHistoryPopUp();
\r
4679 /* [AS] Eval graph */
\r
4680 case IDM_ShowEvalGraph:
\r
4681 if( EvalGraphIsUp() ) {
\r
4682 EvalGraphPopDown();
\r
4686 SetFocus(hwndMain);
\r
4690 /* [AS] Engine output */
\r
4691 case IDM_ShowEngineOutput:
\r
4692 if( EngineOutputIsUp() ) {
\r
4693 EngineOutputPopDown();
\r
4696 EngineOutputPopUp();
\r
4700 /* [AS] User adjudication */
\r
4701 case IDM_UserAdjudication_White:
\r
4702 UserAdjudicationEvent( +1 );
\r
4705 case IDM_UserAdjudication_Black:
\r
4706 UserAdjudicationEvent( -1 );
\r
4709 case IDM_UserAdjudication_Draw:
\r
4710 UserAdjudicationEvent( 0 );
\r
4713 /* [AS] Game list options dialog */
\r
4714 case IDM_GameListOptions:
\r
4715 GameListOptions();
\r
4722 case IDM_CopyPosition:
\r
4723 CopyFENToClipboard();
\r
4726 case IDM_PastePosition:
\r
4727 PasteFENFromClipboard();
\r
4730 case IDM_MailMove:
\r
4734 case IDM_ReloadCMailMsg:
\r
4735 Reset(TRUE, TRUE);
\r
4736 ReloadCmailMsgEvent(FALSE);
\r
4739 case IDM_Minimize:
\r
4740 ShowWindow(hwnd, SW_MINIMIZE);
\r
4747 case IDM_MachineWhite:
\r
4748 MachineWhiteEvent();
\r
4750 * refresh the tags dialog only if it's visible
\r
4752 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4754 tags = PGNTags(&gameInfo);
\r
4755 TagsPopUp(tags, CmailMsg());
\r
4758 SAY("computer starts playing white");
\r
4761 case IDM_MachineBlack:
\r
4762 MachineBlackEvent();
\r
4764 * refresh the tags dialog only if it's visible
\r
4766 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4768 tags = PGNTags(&gameInfo);
\r
4769 TagsPopUp(tags, CmailMsg());
\r
4772 SAY("computer starts playing black");
\r
4775 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
4776 if(gameMode != BeginningOfGame) break; // allow menu item to remain enabled for better mode highligting
\r
4777 matchMode = 2;// distinguish from command-line-triggered case (matchMode=1)
\r
4778 appData.matchGames = appData.defaultMatchGames;
\r
4781 case IDM_TwoMachines:
\r
4782 TwoMachinesEvent();
\r
4784 * refresh the tags dialog only if it's visible
\r
4786 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4788 tags = PGNTags(&gameInfo);
\r
4789 TagsPopUp(tags, CmailMsg());
\r
4792 SAY("computer starts playing both sides");
\r
4795 case IDM_AnalysisMode:
\r
4796 if (!first.analysisSupport) {
\r
4797 sprintf(buf, _("%s does not support analysis"), first.tidy);
\r
4798 DisplayError(buf, 0);
\r
4800 SAY("analyzing current position");
\r
4801 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4802 if (appData.icsActive) {
\r
4803 if (gameMode != IcsObserving) {
\r
4804 sprintf(buf, "You are not observing a game");
\r
4805 DisplayError(buf, 0);
\r
4806 /* secure check */
\r
4807 if (appData.icsEngineAnalyze) {
\r
4808 if (appData.debugMode)
\r
4809 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4810 ExitAnalyzeMode();
\r
4816 /* if enable, user want disable icsEngineAnalyze */
\r
4817 if (appData.icsEngineAnalyze) {
\r
4818 ExitAnalyzeMode();
\r
4822 appData.icsEngineAnalyze = TRUE;
\r
4823 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4826 if (!appData.showThinking) ToggleShowThinking();
\r
4827 AnalyzeModeEvent();
\r
4831 case IDM_AnalyzeFile:
\r
4832 if (!first.analysisSupport) {
\r
4833 char buf[MSG_SIZ];
\r
4834 sprintf(buf, _("%s does not support analysis"), first.tidy);
\r
4835 DisplayError(buf, 0);
\r
4837 if (!appData.showThinking) ToggleShowThinking();
\r
4838 AnalyzeFileEvent();
\r
4839 LoadGameDialog(hwnd, _("Analyze Game from File"));
\r
4840 AnalysisPeriodicEvent(1);
\r
4844 case IDM_IcsClient:
\r
4848 case IDM_EditGame:
\r
4853 case IDM_EditPosition:
\r
4854 EditPositionEvent();
\r
4855 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
4858 case IDM_Training:
\r
4862 case IDM_ShowGameList:
\r
4863 ShowGameListProc();
\r
4866 case IDM_EditTags:
\r
4870 case IDM_EditComment:
\r
4871 if (commentUp && editComment) {
\r
4874 EditCommentEvent();
\r
4894 case IDM_CallFlag:
\r
4914 case IDM_StopObserving:
\r
4915 StopObservingEvent();
\r
4918 case IDM_StopExamining:
\r
4919 StopExaminingEvent();
\r
4923 UploadGameEvent();
\r
4926 case IDM_TypeInMove:
\r
4927 PopUpMoveDialog('\000');
\r
4930 case IDM_TypeInName:
\r
4931 PopUpNameDialog('\000');
\r
4934 case IDM_Backward:
\r
4936 SetFocus(hwndMain);
\r
4943 SetFocus(hwndMain);
\r
4948 SetFocus(hwndMain);
\r
4953 SetFocus(hwndMain);
\r
4957 RevertEvent(FALSE);
\r
4960 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
4961 RevertEvent(TRUE);
\r
4964 case IDM_TruncateGame:
\r
4965 TruncateGameEvent();
\r
4972 case IDM_RetractMove:
\r
4973 RetractMoveEvent();
\r
4976 case IDM_FlipView:
\r
4977 flipView = !flipView;
\r
4978 DrawPosition(FALSE, NULL);
\r
4981 case IDM_FlipClock:
\r
4982 flipClock = !flipClock;
\r
4983 DisplayBothClocks();
\r
4984 DrawPosition(FALSE, NULL);
\r
4987 case IDM_MuteSounds:
\r
4988 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
4989 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
4990 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
4993 case IDM_GeneralOptions:
\r
4994 GeneralOptionsPopup(hwnd);
\r
4995 DrawPosition(TRUE, NULL);
\r
4998 case IDM_BoardOptions:
\r
4999 BoardOptionsPopup(hwnd);
\r
5002 case IDM_EnginePlayOptions:
\r
5003 EnginePlayOptionsPopup(hwnd);
\r
5006 case IDM_Engine1Options:
\r
5007 EngineOptionsPopup(hwnd, &first);
\r
5010 case IDM_Engine2Options:
\r
5011 EngineOptionsPopup(hwnd, &second);
\r
5014 case IDM_OptionsUCI:
\r
5015 UciOptionsPopup(hwnd);
\r
5018 case IDM_IcsOptions:
\r
5019 IcsOptionsPopup(hwnd);
\r
5023 FontsOptionsPopup(hwnd);
\r
5027 SoundOptionsPopup(hwnd);
\r
5030 case IDM_CommPort:
\r
5031 CommPortOptionsPopup(hwnd);
\r
5034 case IDM_LoadOptions:
\r
5035 LoadOptionsPopup(hwnd);
\r
5038 case IDM_SaveOptions:
\r
5039 SaveOptionsPopup(hwnd);
\r
5042 case IDM_TimeControl:
\r
5043 TimeControlOptionsPopup(hwnd);
\r
5046 case IDM_SaveSettings:
\r
5047 SaveSettings(settingsFileName);
\r
5050 case IDM_SaveSettingsOnExit:
\r
5051 saveSettingsOnExit = !saveSettingsOnExit;
\r
5052 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5053 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5054 MF_CHECKED : MF_UNCHECKED));
\r
5065 case IDM_AboutGame:
\r
5070 appData.debugMode = !appData.debugMode;
\r
5071 if (appData.debugMode) {
\r
5072 char dir[MSG_SIZ];
\r
5073 GetCurrentDirectory(MSG_SIZ, dir);
\r
5074 SetCurrentDirectory(installDir);
\r
5075 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5076 SetCurrentDirectory(dir);
\r
5077 setbuf(debugFP, NULL);
\r
5084 case IDM_HELPCONTENTS:
\r
5085 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5086 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5087 MessageBox (GetFocus(),
\r
5088 _("Unable to activate help"),
\r
5089 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5093 case IDM_HELPSEARCH:
\r
5094 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5095 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5096 MessageBox (GetFocus(),
\r
5097 _("Unable to activate help"),
\r
5098 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5102 case IDM_HELPHELP:
\r
5103 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5104 MessageBox (GetFocus(),
\r
5105 _("Unable to activate help"),
\r
5106 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5111 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5113 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5114 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5115 FreeProcInstance(lpProc);
\r
5118 case IDM_DirectCommand1:
\r
5119 AskQuestionEvent(_("Direct Command"),
\r
5120 _("Send to chess program:"), "", "1");
\r
5122 case IDM_DirectCommand2:
\r
5123 AskQuestionEvent(_("Direct Command"),
\r
5124 _("Send to second chess program:"), "", "2");
\r
5127 case EP_WhitePawn:
\r
5128 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5129 fromX = fromY = -1;
\r
5132 case EP_WhiteKnight:
\r
5133 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5134 fromX = fromY = -1;
\r
5137 case EP_WhiteBishop:
\r
5138 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5139 fromX = fromY = -1;
\r
5142 case EP_WhiteRook:
\r
5143 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5144 fromX = fromY = -1;
\r
5147 case EP_WhiteQueen:
\r
5148 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5149 fromX = fromY = -1;
\r
5152 case EP_WhiteFerz:
\r
5153 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5154 fromX = fromY = -1;
\r
5157 case EP_WhiteWazir:
\r
5158 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5159 fromX = fromY = -1;
\r
5162 case EP_WhiteAlfil:
\r
5163 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5164 fromX = fromY = -1;
\r
5167 case EP_WhiteCannon:
\r
5168 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5169 fromX = fromY = -1;
\r
5172 case EP_WhiteCardinal:
\r
5173 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5174 fromX = fromY = -1;
\r
5177 case EP_WhiteMarshall:
\r
5178 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5179 fromX = fromY = -1;
\r
5182 case EP_WhiteKing:
\r
5183 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5184 fromX = fromY = -1;
\r
5187 case EP_BlackPawn:
\r
5188 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5189 fromX = fromY = -1;
\r
5192 case EP_BlackKnight:
\r
5193 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5194 fromX = fromY = -1;
\r
5197 case EP_BlackBishop:
\r
5198 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5199 fromX = fromY = -1;
\r
5202 case EP_BlackRook:
\r
5203 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5204 fromX = fromY = -1;
\r
5207 case EP_BlackQueen:
\r
5208 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5209 fromX = fromY = -1;
\r
5212 case EP_BlackFerz:
\r
5213 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5214 fromX = fromY = -1;
\r
5217 case EP_BlackWazir:
\r
5218 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5219 fromX = fromY = -1;
\r
5222 case EP_BlackAlfil:
\r
5223 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5224 fromX = fromY = -1;
\r
5227 case EP_BlackCannon:
\r
5228 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5229 fromX = fromY = -1;
\r
5232 case EP_BlackCardinal:
\r
5233 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5234 fromX = fromY = -1;
\r
5237 case EP_BlackMarshall:
\r
5238 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5239 fromX = fromY = -1;
\r
5242 case EP_BlackKing:
\r
5243 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5244 fromX = fromY = -1;
\r
5247 case EP_EmptySquare:
\r
5248 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5249 fromX = fromY = -1;
\r
5252 case EP_ClearBoard:
\r
5253 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5254 fromX = fromY = -1;
\r
5258 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5259 fromX = fromY = -1;
\r
5263 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5264 fromX = fromY = -1;
\r
5268 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5269 fromX = fromY = -1;
\r
5273 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5274 fromX = fromY = -1;
\r
5278 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5279 fromX = fromY = -1;
\r
5283 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5284 fromX = fromY = -1;
\r
5288 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5289 fromX = fromY = -1;
\r
5293 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5294 fromX = fromY = -1;
\r
5298 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5299 fromX = fromY = -1;
\r
5303 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5309 case CLOCK_TIMER_ID:
\r
5310 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5311 clockTimerEvent = 0;
\r
5312 DecrementClocks(); /* call into back end */
\r
5314 case LOAD_GAME_TIMER_ID:
\r
5315 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5316 loadGameTimerEvent = 0;
\r
5317 AutoPlayGameLoop(); /* call into back end */
\r
5319 case ANALYSIS_TIMER_ID:
\r
5320 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5321 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5322 AnalysisPeriodicEvent(0);
\r
5324 KillTimer(hwnd, analysisTimerEvent);
\r
5325 analysisTimerEvent = 0;
\r
5328 case DELAYED_TIMER_ID:
\r
5329 KillTimer(hwnd, delayedTimerEvent);
\r
5330 delayedTimerEvent = 0;
\r
5331 delayedTimerCallback();
\r
5336 case WM_USER_Input:
\r
5337 InputEvent(hwnd, message, wParam, lParam);
\r
5340 /* [AS] Also move "attached" child windows */
\r
5341 case WM_WINDOWPOSCHANGING:
\r
5343 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5344 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5346 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5347 /* Window is moving */
\r
5350 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5351 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5352 rcMain.right = wpMain.x + wpMain.width;
\r
5353 rcMain.top = wpMain.y;
\r
5354 rcMain.bottom = wpMain.y + wpMain.height;
\r
5356 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5357 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5358 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5359 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5360 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5361 wpMain.x = lpwp->x;
\r
5362 wpMain.y = lpwp->y;
\r
5367 /* [AS] Snapping */
\r
5368 case WM_ENTERSIZEMOVE:
\r
5369 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5370 if (hwnd == hwndMain) {
\r
5371 doingSizing = TRUE;
\r
5374 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5378 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5379 if (hwnd == hwndMain) {
\r
5380 lastSizing = wParam;
\r
5385 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5386 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5388 case WM_EXITSIZEMOVE:
\r
5389 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5390 if (hwnd == hwndMain) {
\r
5392 doingSizing = FALSE;
\r
5393 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5394 GetClientRect(hwnd, &client);
\r
5395 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5397 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5399 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5402 case WM_DESTROY: /* message: window being destroyed */
\r
5403 PostQuitMessage(0);
\r
5407 if (hwnd == hwndMain) {
\r
5412 default: /* Passes it on if unprocessed */
\r
5413 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5418 /*---------------------------------------------------------------------------*\
\r
5420 * Misc utility routines
\r
5422 \*---------------------------------------------------------------------------*/
\r
5425 * Decent random number generator, at least not as bad as Windows
\r
5426 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5428 unsigned int randstate;
\r
5433 randstate = randstate * 1664525 + 1013904223;
\r
5434 return (int) randstate & 0x7fffffff;
\r
5438 mysrandom(unsigned int seed)
\r
5445 * returns TRUE if user selects a different color, FALSE otherwise
\r
5449 ChangeColor(HWND hwnd, COLORREF *which)
\r
5451 static BOOL firstTime = TRUE;
\r
5452 static DWORD customColors[16];
\r
5454 COLORREF newcolor;
\r
5459 /* Make initial colors in use available as custom colors */
\r
5460 /* Should we put the compiled-in defaults here instead? */
\r
5462 customColors[i++] = lightSquareColor & 0xffffff;
\r
5463 customColors[i++] = darkSquareColor & 0xffffff;
\r
5464 customColors[i++] = whitePieceColor & 0xffffff;
\r
5465 customColors[i++] = blackPieceColor & 0xffffff;
\r
5466 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5467 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5469 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5470 customColors[i++] = textAttribs[ccl].color;
\r
5472 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5473 firstTime = FALSE;
\r
5476 cc.lStructSize = sizeof(cc);
\r
5477 cc.hwndOwner = hwnd;
\r
5478 cc.hInstance = NULL;
\r
5479 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5480 cc.lpCustColors = (LPDWORD) customColors;
\r
5481 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5483 if (!ChooseColor(&cc)) return FALSE;
\r
5485 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5486 if (newcolor == *which) return FALSE;
\r
5487 *which = newcolor;
\r
5491 InitDrawingColors();
\r
5492 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5497 MyLoadSound(MySound *ms)
\r
5503 if (ms->data) free(ms->data);
\r
5506 switch (ms->name[0]) {
\r
5512 /* System sound from Control Panel. Don't preload here. */
\r
5516 if (ms->name[1] == NULLCHAR) {
\r
5517 /* "!" alone = silence */
\r
5520 /* Builtin wave resource. Error if not found. */
\r
5521 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5522 if (h == NULL) break;
\r
5523 ms->data = (void *)LoadResource(hInst, h);
\r
5524 if (h == NULL) break;
\r
5529 /* .wav file. Error if not found. */
\r
5530 f = fopen(ms->name, "rb");
\r
5531 if (f == NULL) break;
\r
5532 if (fstat(fileno(f), &st) < 0) break;
\r
5533 ms->data = malloc(st.st_size);
\r
5534 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5540 char buf[MSG_SIZ];
\r
5541 sprintf(buf, _("Error loading sound %s"), ms->name);
\r
5542 DisplayError(buf, GetLastError());
\r
5548 MyPlaySound(MySound *ms)
\r
5550 BOOLEAN ok = FALSE;
\r
5552 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5553 switch (ms->name[0]) {
\r
5555 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5560 /* System sound from Control Panel (deprecated feature).
\r
5561 "$" alone or an unset sound name gets default beep (still in use). */
\r
5562 if (ms->name[1]) {
\r
5563 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5565 if (!ok) ok = MessageBeep(MB_OK);
\r
5568 /* Builtin wave resource, or "!" alone for silence */
\r
5569 if (ms->name[1]) {
\r
5570 if (ms->data == NULL) return FALSE;
\r
5571 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5577 /* .wav file. Error if not found. */
\r
5578 if (ms->data == NULL) return FALSE;
\r
5579 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5582 /* Don't print an error: this can happen innocently if the sound driver
\r
5583 is busy; for instance, if another instance of WinBoard is playing
\r
5584 a sound at about the same time. */
\r
5590 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5593 OPENFILENAME *ofn;
\r
5594 static UINT *number; /* gross that this is static */
\r
5596 switch (message) {
\r
5597 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5598 /* Center the dialog over the application window */
\r
5599 ofn = (OPENFILENAME *) lParam;
\r
5600 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5601 number = (UINT *) ofn->lCustData;
\r
5602 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5606 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5607 Translate(hDlg, 1536);
\r
5608 return FALSE; /* Allow for further processing */
\r
5611 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5612 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5614 return FALSE; /* Allow for further processing */
\r
5620 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5622 static UINT *number;
\r
5623 OPENFILENAME *ofname;
\r
5626 case WM_INITDIALOG:
\r
5627 Translate(hdlg, DLG_IndexNumber);
\r
5628 ofname = (OPENFILENAME *)lParam;
\r
5629 number = (UINT *)(ofname->lCustData);
\r
5632 ofnot = (OFNOTIFY *)lParam;
\r
5633 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5634 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5643 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5644 char *nameFilt, char *dlgTitle, UINT *number,
\r
5645 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5647 OPENFILENAME openFileName;
\r
5648 char buf1[MSG_SIZ];
\r
5651 if (fileName == NULL) fileName = buf1;
\r
5652 if (defName == NULL) {
\r
5653 strcpy(fileName, "*.");
\r
5654 strcat(fileName, defExt);
\r
5656 strcpy(fileName, defName);
\r
5658 if (fileTitle) strcpy(fileTitle, "");
\r
5659 if (number) *number = 0;
\r
5661 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5662 openFileName.hwndOwner = hwnd;
\r
5663 openFileName.hInstance = (HANDLE) hInst;
\r
5664 openFileName.lpstrFilter = nameFilt;
\r
5665 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5666 openFileName.nMaxCustFilter = 0L;
\r
5667 openFileName.nFilterIndex = 1L;
\r
5668 openFileName.lpstrFile = fileName;
\r
5669 openFileName.nMaxFile = MSG_SIZ;
\r
5670 openFileName.lpstrFileTitle = fileTitle;
\r
5671 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5672 openFileName.lpstrInitialDir = NULL;
\r
5673 openFileName.lpstrTitle = dlgTitle;
\r
5674 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5675 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5676 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5677 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5678 openFileName.nFileOffset = 0;
\r
5679 openFileName.nFileExtension = 0;
\r
5680 openFileName.lpstrDefExt = defExt;
\r
5681 openFileName.lCustData = (LONG) number;
\r
5682 openFileName.lpfnHook = oldDialog ?
\r
5683 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5684 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5686 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5687 GetOpenFileName(&openFileName)) {
\r
5688 /* open the file */
\r
5689 f = fopen(openFileName.lpstrFile, write);
\r
5691 MessageBox(hwnd, _("File open failed"), NULL,
\r
5692 MB_OK|MB_ICONEXCLAMATION);
\r
5696 int err = CommDlgExtendedError();
\r
5697 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5706 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5708 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5711 * Get the first pop-up menu in the menu template. This is the
\r
5712 * menu that TrackPopupMenu displays.
\r
5714 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5716 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5719 * TrackPopup uses screen coordinates, so convert the
\r
5720 * coordinates of the mouse click to screen coordinates.
\r
5722 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5724 /* Draw and track the floating pop-up menu. */
\r
5725 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5726 pt.x, pt.y, 0, hwnd, NULL);
\r
5728 /* Destroy the menu.*/
\r
5729 DestroyMenu(hmenu);
\r
5734 int sizeX, sizeY, newSizeX, newSizeY;
\r
5736 } ResizeEditPlusButtonsClosure;
\r
5739 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5741 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5745 if (hChild == cl->hText) return TRUE;
\r
5746 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5747 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5748 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5749 ScreenToClient(cl->hDlg, &pt);
\r
5750 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5751 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5755 /* Resize a dialog that has a (rich) edit field filling most of
\r
5756 the top, with a row of buttons below */
\r
5758 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5761 int newTextHeight, newTextWidth;
\r
5762 ResizeEditPlusButtonsClosure cl;
\r
5764 /*if (IsIconic(hDlg)) return;*/
\r
5765 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5767 cl.hdwp = BeginDeferWindowPos(8);
\r
5769 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5770 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5771 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5772 if (newTextHeight < 0) {
\r
5773 newSizeY += -newTextHeight;
\r
5774 newTextHeight = 0;
\r
5776 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5777 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5783 cl.newSizeX = newSizeX;
\r
5784 cl.newSizeY = newSizeY;
\r
5785 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5787 EndDeferWindowPos(cl.hdwp);
\r
5790 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5792 RECT rChild, rParent;
\r
5793 int wChild, hChild, wParent, hParent;
\r
5794 int wScreen, hScreen, xNew, yNew;
\r
5797 /* Get the Height and Width of the child window */
\r
5798 GetWindowRect (hwndChild, &rChild);
\r
5799 wChild = rChild.right - rChild.left;
\r
5800 hChild = rChild.bottom - rChild.top;
\r
5802 /* Get the Height and Width of the parent window */
\r
5803 GetWindowRect (hwndParent, &rParent);
\r
5804 wParent = rParent.right - rParent.left;
\r
5805 hParent = rParent.bottom - rParent.top;
\r
5807 /* Get the display limits */
\r
5808 hdc = GetDC (hwndChild);
\r
5809 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5810 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5811 ReleaseDC(hwndChild, hdc);
\r
5813 /* Calculate new X position, then adjust for screen */
\r
5814 xNew = rParent.left + ((wParent - wChild) /2);
\r
5817 } else if ((xNew+wChild) > wScreen) {
\r
5818 xNew = wScreen - wChild;
\r
5821 /* Calculate new Y position, then adjust for screen */
\r
5823 yNew = rParent.top + ((hParent - hChild) /2);
\r
5826 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5831 } else if ((yNew+hChild) > hScreen) {
\r
5832 yNew = hScreen - hChild;
\r
5835 /* Set it, and return */
\r
5836 return SetWindowPos (hwndChild, NULL,
\r
5837 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
5840 /* Center one window over another */
\r
5841 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
5843 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
5846 /*---------------------------------------------------------------------------*\
\r
5848 * Startup Dialog functions
\r
5850 \*---------------------------------------------------------------------------*/
\r
5852 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
5854 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5856 while (*cd != NULL) {
\r
5857 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
5863 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
5865 char buf1[MAX_ARG_LEN];
\r
5868 if (str[0] == '@') {
\r
5869 FILE* f = fopen(str + 1, "r");
\r
5871 DisplayFatalError(str + 1, errno, 2);
\r
5874 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
5876 buf1[len] = NULLCHAR;
\r
5880 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5883 char buf[MSG_SIZ];
\r
5884 char *end = strchr(str, '\n');
\r
5885 if (end == NULL) return;
\r
5886 memcpy(buf, str, end - str);
\r
5887 buf[end - str] = NULLCHAR;
\r
5888 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
5894 SetStartupDialogEnables(HWND hDlg)
\r
5896 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5897 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5898 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
5899 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5900 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
5901 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
5902 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
5903 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
5904 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
5905 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
5906 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5907 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
5908 IsDlgButtonChecked(hDlg, OPT_View));
\r
5912 QuoteForFilename(char *filename)
\r
5914 int dquote, space;
\r
5915 dquote = strchr(filename, '"') != NULL;
\r
5916 space = strchr(filename, ' ') != NULL;
\r
5917 if (dquote || space) {
\r
5929 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
5931 char buf[MSG_SIZ];
\r
5934 InitComboStringsFromOption(hwndCombo, nthnames);
\r
5935 q = QuoteForFilename(nthcp);
\r
5936 sprintf(buf, "%s%s%s", q, nthcp, q);
\r
5937 if (*nthdir != NULLCHAR) {
\r
5938 q = QuoteForFilename(nthdir);
\r
5939 sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);
\r
5941 if (*nthcp == NULLCHAR) {
\r
5942 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
5943 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
5944 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
5945 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
5950 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5952 char buf[MSG_SIZ];
\r
5956 switch (message) {
\r
5957 case WM_INITDIALOG:
\r
5958 /* Center the dialog */
\r
5959 CenterWindow (hDlg, GetDesktopWindow());
\r
5960 Translate(hDlg, DLG_Startup);
\r
5961 /* Initialize the dialog items */
\r
5962 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5963 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
5964 firstChessProgramNames);
\r
5965 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5966 appData.secondChessProgram, "sd", appData.secondDirectory,
\r
5967 secondChessProgramNames);
\r
5968 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
5969 InitComboStringsFromOption(hwndCombo, icsNames);
\r
5970 sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
5971 if (*appData.icsHelper != NULLCHAR) {
\r
5972 char *q = QuoteForFilename(appData.icsHelper);
\r
5973 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
5975 if (*appData.icsHost == NULLCHAR) {
\r
5976 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
5977 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
5978 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
5979 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
5980 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
5983 if (appData.icsActive) {
\r
5984 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
5986 else if (appData.noChessProgram) {
\r
5987 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
5990 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
5993 SetStartupDialogEnables(hDlg);
\r
5997 switch (LOWORD(wParam)) {
\r
5999 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6000 strcpy(buf, "/fcp=");
\r
6001 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6003 ParseArgs(StringGet, &p);
\r
6004 strcpy(buf, "/scp=");
\r
6005 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6007 ParseArgs(StringGet, &p);
\r
6008 appData.noChessProgram = FALSE;
\r
6009 appData.icsActive = FALSE;
\r
6010 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6011 strcpy(buf, "/ics /icshost=");
\r
6012 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6014 ParseArgs(StringGet, &p);
\r
6015 if (appData.zippyPlay) {
\r
6016 strcpy(buf, "/fcp=");
\r
6017 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6019 ParseArgs(StringGet, &p);
\r
6021 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6022 appData.noChessProgram = TRUE;
\r
6023 appData.icsActive = FALSE;
\r
6025 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6026 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6029 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6030 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6032 ParseArgs(StringGet, &p);
\r
6034 EndDialog(hDlg, TRUE);
\r
6041 case IDM_HELPCONTENTS:
\r
6042 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6043 MessageBox (GetFocus(),
\r
6044 _("Unable to activate help"),
\r
6045 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6050 SetStartupDialogEnables(hDlg);
\r
6058 /*---------------------------------------------------------------------------*\
\r
6060 * About box dialog functions
\r
6062 \*---------------------------------------------------------------------------*/
\r
6064 /* Process messages for "About" dialog box */
\r
6066 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6068 switch (message) {
\r
6069 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6070 /* Center the dialog over the application window */
\r
6071 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6072 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6073 Translate(hDlg, ABOUTBOX);
\r
6077 case WM_COMMAND: /* message: received a command */
\r
6078 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6079 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6080 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6088 /*---------------------------------------------------------------------------*\
\r
6090 * Comment Dialog functions
\r
6092 \*---------------------------------------------------------------------------*/
\r
6095 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6097 static HANDLE hwndText = NULL;
\r
6098 int len, newSizeX, newSizeY, flags;
\r
6099 static int sizeX, sizeY;
\r
6104 switch (message) {
\r
6105 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6106 /* Initialize the dialog items */
\r
6107 Translate(hDlg, DLG_EditComment);
\r
6108 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6109 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6110 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6111 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6112 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6113 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6114 SetWindowText(hDlg, commentTitle);
\r
6115 if (editComment) {
\r
6116 SetFocus(hwndText);
\r
6118 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6120 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6121 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6122 MAKELPARAM(FALSE, 0));
\r
6123 /* Size and position the dialog */
\r
6124 if (!commentDialog) {
\r
6125 commentDialog = hDlg;
\r
6126 flags = SWP_NOZORDER;
\r
6127 GetClientRect(hDlg, &rect);
\r
6128 sizeX = rect.right;
\r
6129 sizeY = rect.bottom;
\r
6130 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6131 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6132 WINDOWPLACEMENT wp;
\r
6133 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6134 wp.length = sizeof(WINDOWPLACEMENT);
\r
6136 wp.showCmd = SW_SHOW;
\r
6137 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6138 wp.rcNormalPosition.left = wpComment.x;
\r
6139 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6140 wp.rcNormalPosition.top = wpComment.y;
\r
6141 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6142 SetWindowPlacement(hDlg, &wp);
\r
6144 GetClientRect(hDlg, &rect);
\r
6145 newSizeX = rect.right;
\r
6146 newSizeY = rect.bottom;
\r
6147 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6148 newSizeX, newSizeY);
\r
6153 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );
\r
6156 case WM_COMMAND: /* message: received a command */
\r
6157 switch (LOWORD(wParam)) {
\r
6159 if (editComment) {
\r
6161 /* Read changed options from the dialog box */
\r
6162 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6163 len = GetWindowTextLength(hwndText);
\r
6164 str = (char *) malloc(len + 1);
\r
6165 GetWindowText(hwndText, str, len + 1);
\r
6174 ReplaceComment(commentIndex, str);
\r
6181 case OPT_CancelComment:
\r
6185 case OPT_ClearComment:
\r
6186 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6189 case OPT_EditComment:
\r
6190 EditCommentEvent();
\r
6198 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6199 if( wParam == OPT_CommentText ) {
\r
6200 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6202 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ) {
\r
6206 pt.x = LOWORD( lpMF->lParam );
\r
6207 pt.y = HIWORD( lpMF->lParam );
\r
6209 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6211 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6212 len = GetWindowTextLength(hwndText);
\r
6213 str = (char *) malloc(len + 1);
\r
6214 GetWindowText(hwndText, str, len + 1);
\r
6215 ReplaceComment(commentIndex, str);
\r
6216 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6217 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6220 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6221 lpMF->msg = WM_USER;
\r
6229 newSizeX = LOWORD(lParam);
\r
6230 newSizeY = HIWORD(lParam);
\r
6231 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6236 case WM_GETMINMAXINFO:
\r
6237 /* Prevent resizing window too small */
\r
6238 mmi = (MINMAXINFO *) lParam;
\r
6239 mmi->ptMinTrackSize.x = 100;
\r
6240 mmi->ptMinTrackSize.y = 100;
\r
6247 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6252 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6254 if (str == NULL) str = "";
\r
6255 p = (char *) malloc(2 * strlen(str) + 2);
\r
6258 if (*str == '\n') *q++ = '\r';
\r
6262 if (commentText != NULL) free(commentText);
\r
6264 commentIndex = index;
\r
6265 commentTitle = title;
\r
6267 editComment = edit;
\r
6269 if (commentDialog) {
\r
6270 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6271 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6273 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6274 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6275 hwndMain, (DLGPROC)lpProc);
\r
6276 FreeProcInstance(lpProc);
\r
6282 /*---------------------------------------------------------------------------*\
\r
6284 * Type-in move dialog functions
\r
6286 \*---------------------------------------------------------------------------*/
\r
6289 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6291 char move[MSG_SIZ];
\r
6293 ChessMove moveType;
\r
6294 int fromX, fromY, toX, toY;
\r
6297 switch (message) {
\r
6298 case WM_INITDIALOG:
\r
6299 move[0] = (char) lParam;
\r
6300 move[1] = NULLCHAR;
\r
6301 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6302 Translate(hDlg, DLG_TypeInMove);
\r
6303 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6304 SetWindowText(hInput, move);
\r
6306 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6310 switch (LOWORD(wParam)) {
\r
6312 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6313 { int n; Board board;
\r
6315 if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {
\r
6316 EditPositionPasteFEN(move);
\r
6317 EndDialog(hDlg, TRUE);
\r
6320 // [HGM] movenum: allow move number to be typed in any mode
\r
6321 if(sscanf(move, "%d", &n) == 1 && n != 0 ) {
\r
6323 EndDialog(hDlg, TRUE);
\r
6327 if (gameMode != EditGame && currentMove != forwardMostMove &&
\r
6328 gameMode != Training) {
\r
6329 DisplayMoveError(_("Displayed move is not current"));
\r
6331 // GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream
\r
6332 int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6333 &moveType, &fromX, &fromY, &toX, &toY, &promoChar);
\r
6334 if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized
\r
6335 if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6336 &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {
\r
6337 if (gameMode != Training)
\r
6338 forwardMostMove = currentMove;
\r
6339 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
6341 DisplayMoveError(_("Could not parse move"));
\r
6344 EndDialog(hDlg, TRUE);
\r
6347 EndDialog(hDlg, FALSE);
\r
6358 PopUpMoveDialog(char firstchar)
\r
6362 if ((gameMode == BeginningOfGame && !appData.icsActive) ||
\r
6363 gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
\r
6364 gameMode == AnalyzeMode || gameMode == EditGame ||
\r
6365 gameMode == EditPosition || gameMode == IcsExamining ||
\r
6366 gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
\r
6367 isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes
\r
6368 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||
\r
6369 gameMode == IcsObserving || gameMode == TwoMachinesPlay ) ||
\r
6370 gameMode == Training) {
\r
6371 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6372 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6373 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6374 FreeProcInstance(lpProc);
\r
6378 /*---------------------------------------------------------------------------*\
\r
6380 * Type-in name dialog functions
\r
6382 \*---------------------------------------------------------------------------*/
\r
6385 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6387 char move[MSG_SIZ];
\r
6390 switch (message) {
\r
6391 case WM_INITDIALOG:
\r
6392 move[0] = (char) lParam;
\r
6393 move[1] = NULLCHAR;
\r
6394 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6395 Translate(hDlg, DLG_TypeInName);
\r
6396 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6397 SetWindowText(hInput, move);
\r
6399 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6403 switch (LOWORD(wParam)) {
\r
6405 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6406 appData.userName = strdup(move);
\r
6409 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6410 sprintf(move, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6411 DisplayTitle(move);
\r
6415 EndDialog(hDlg, TRUE);
\r
6418 EndDialog(hDlg, FALSE);
\r
6429 PopUpNameDialog(char firstchar)
\r
6433 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6434 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6435 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6436 FreeProcInstance(lpProc);
\r
6439 /*---------------------------------------------------------------------------*\
\r
6443 \*---------------------------------------------------------------------------*/
\r
6445 /* Nonmodal error box */
\r
6446 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6447 WPARAM wParam, LPARAM lParam);
\r
6450 ErrorPopUp(char *title, char *content)
\r
6454 BOOLEAN modal = hwndMain == NULL;
\r
6472 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6473 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6476 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6478 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6479 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6480 hwndMain, (DLGPROC)lpProc);
\r
6481 FreeProcInstance(lpProc);
\r
6488 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6489 if (errorDialog == NULL) return;
\r
6490 DestroyWindow(errorDialog);
\r
6491 errorDialog = NULL;
\r
6492 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6496 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6501 switch (message) {
\r
6502 case WM_INITDIALOG:
\r
6503 GetWindowRect(hDlg, &rChild);
\r
6506 SetWindowPos(hDlg, NULL, rChild.left,
\r
6507 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6508 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6512 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6513 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6514 and it doesn't work when you resize the dialog.
\r
6515 For now, just give it a default position.
\r
6517 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6518 Translate(hDlg, DLG_Error);
\r
6520 errorDialog = hDlg;
\r
6521 SetWindowText(hDlg, errorTitle);
\r
6522 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6523 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6527 switch (LOWORD(wParam)) {
\r
6530 if (errorDialog == hDlg) errorDialog = NULL;
\r
6531 DestroyWindow(hDlg);
\r
6543 HWND gothicDialog = NULL;
\r
6546 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6550 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6552 switch (message) {
\r
6553 case WM_INITDIALOG:
\r
6554 GetWindowRect(hDlg, &rChild);
\r
6556 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6560 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6561 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6562 and it doesn't work when you resize the dialog.
\r
6563 For now, just give it a default position.
\r
6565 gothicDialog = hDlg;
\r
6566 SetWindowText(hDlg, errorTitle);
\r
6567 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6568 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6572 switch (LOWORD(wParam)) {
\r
6575 if (errorDialog == hDlg) errorDialog = NULL;
\r
6576 DestroyWindow(hDlg);
\r
6588 GothicPopUp(char *title, VariantClass variant)
\r
6591 static char *lastTitle;
\r
6593 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6594 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6596 if(lastTitle != title && gothicDialog != NULL) {
\r
6597 DestroyWindow(gothicDialog);
\r
6598 gothicDialog = NULL;
\r
6600 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6601 title = lastTitle;
\r
6602 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6603 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6604 hwndMain, (DLGPROC)lpProc);
\r
6605 FreeProcInstance(lpProc);
\r
6610 /*---------------------------------------------------------------------------*\
\r
6612 * Ics Interaction console functions
\r
6614 \*---------------------------------------------------------------------------*/
\r
6616 #define HISTORY_SIZE 64
\r
6617 static char *history[HISTORY_SIZE];
\r
6618 int histIn = 0, histP = 0;
\r
6621 SaveInHistory(char *cmd)
\r
6623 if (history[histIn] != NULL) {
\r
6624 free(history[histIn]);
\r
6625 history[histIn] = NULL;
\r
6627 if (*cmd == NULLCHAR) return;
\r
6628 history[histIn] = StrSave(cmd);
\r
6629 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6630 if (history[histIn] != NULL) {
\r
6631 free(history[histIn]);
\r
6632 history[histIn] = NULL;
\r
6638 PrevInHistory(char *cmd)
\r
6641 if (histP == histIn) {
\r
6642 if (history[histIn] != NULL) free(history[histIn]);
\r
6643 history[histIn] = StrSave(cmd);
\r
6645 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6646 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6648 return history[histP];
\r
6654 if (histP == histIn) return NULL;
\r
6655 histP = (histP + 1) % HISTORY_SIZE;
\r
6656 return history[histP];
\r
6660 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6664 hmenu = LoadMenu(hInst, "TextMenu");
\r
6665 h = GetSubMenu(hmenu, 0);
\r
6667 if (strcmp(e->item, "-") == 0) {
\r
6668 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6669 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6670 int flags = MF_STRING, j = 0;
\r
6671 if (e->item[0] == '|') {
\r
6672 flags |= MF_MENUBARBREAK;
\r
6675 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6676 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6684 WNDPROC consoleTextWindowProc;
\r
6687 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6689 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6690 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6694 SetWindowText(hInput, command);
\r
6696 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6698 sel.cpMin = 999999;
\r
6699 sel.cpMax = 999999;
\r
6700 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6705 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6706 if (sel.cpMin == sel.cpMax) {
\r
6707 /* Expand to surrounding word */
\r
6710 tr.chrg.cpMax = sel.cpMin;
\r
6711 tr.chrg.cpMin = --sel.cpMin;
\r
6712 if (sel.cpMin < 0) break;
\r
6713 tr.lpstrText = name;
\r
6714 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6715 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6719 tr.chrg.cpMin = sel.cpMax;
\r
6720 tr.chrg.cpMax = ++sel.cpMax;
\r
6721 tr.lpstrText = name;
\r
6722 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6723 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6726 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6727 MessageBeep(MB_ICONEXCLAMATION);
\r
6731 tr.lpstrText = name;
\r
6732 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6734 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6735 MessageBeep(MB_ICONEXCLAMATION);
\r
6738 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6741 sprintf(buf, "%s %s", command, name);
\r
6742 SetWindowText(hInput, buf);
\r
6743 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6745 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6746 sprintf(buf, "%s %s ", command, name); /* trailing space */
\r
6747 SetWindowText(hInput, buf);
\r
6748 sel.cpMin = 999999;
\r
6749 sel.cpMax = 999999;
\r
6750 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6756 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6761 switch (message) {
\r
6763 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6766 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6769 sel.cpMin = 999999;
\r
6770 sel.cpMax = 999999;
\r
6771 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6772 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6777 if(wParam != '\022') {
\r
6778 if (wParam == '\t') {
\r
6779 if (GetKeyState(VK_SHIFT) < 0) {
\r
6781 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6782 if (buttonDesc[0].hwnd) {
\r
6783 SetFocus(buttonDesc[0].hwnd);
\r
6785 SetFocus(hwndMain);
\r
6789 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6792 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6793 JAWS_DELETE( SetFocus(hInput); )
\r
6794 SendMessage(hInput, message, wParam, lParam);
\r
6797 } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu
\r
6798 case WM_RBUTTONDOWN:
\r
6799 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6800 /* Move selection here if it was empty */
\r
6802 pt.x = LOWORD(lParam);
\r
6803 pt.y = HIWORD(lParam);
\r
6804 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6805 if (sel.cpMin == sel.cpMax) {
\r
6806 sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6807 sel.cpMax = sel.cpMin;
\r
6808 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6810 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6811 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6813 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6814 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6815 if (sel.cpMin == sel.cpMax) {
\r
6816 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6817 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6819 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6820 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6822 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6823 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6824 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6825 MenuPopup(hwnd, pt, hmenu, -1);
\r
6829 case WM_RBUTTONUP:
\r
6830 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6831 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6832 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6836 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6838 return SendMessage(hInput, message, wParam, lParam);
\r
6839 case WM_MBUTTONDOWN:
\r
6840 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6842 switch (LOWORD(wParam)) {
\r
6843 case IDM_QuickPaste:
\r
6845 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6846 if (sel.cpMin == sel.cpMax) {
\r
6847 MessageBeep(MB_ICONEXCLAMATION);
\r
6850 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6851 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6852 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6857 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6860 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6863 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6867 int i = LOWORD(wParam) - IDM_CommandX;
\r
6868 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
6869 icsTextMenuEntry[i].command != NULL) {
\r
6870 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
6871 icsTextMenuEntry[i].getname,
\r
6872 icsTextMenuEntry[i].immediate);
\r
6880 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
6883 WNDPROC consoleInputWindowProc;
\r
6886 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6888 char buf[MSG_SIZ];
\r
6890 static BOOL sendNextChar = FALSE;
\r
6891 static BOOL quoteNextChar = FALSE;
\r
6892 InputSource *is = consoleInputSource;
\r
6896 switch (message) {
\r
6898 if (!appData.localLineEditing || sendNextChar) {
\r
6899 is->buf[0] = (CHAR) wParam;
\r
6901 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6902 sendNextChar = FALSE;
\r
6905 if (quoteNextChar) {
\r
6906 buf[0] = (char) wParam;
\r
6907 buf[1] = NULLCHAR;
\r
6908 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
6909 quoteNextChar = FALSE;
\r
6913 case '\r': /* Enter key */
\r
6914 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
6915 if (consoleEcho) SaveInHistory(is->buf);
\r
6916 is->buf[is->count++] = '\n';
\r
6917 is->buf[is->count] = NULLCHAR;
\r
6918 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6919 if (consoleEcho) {
\r
6920 ConsoleOutput(is->buf, is->count, TRUE);
\r
6921 } else if (appData.localLineEditing) {
\r
6922 ConsoleOutput("\n", 1, TRUE);
\r
6925 case '\033': /* Escape key */
\r
6926 SetWindowText(hwnd, "");
\r
6927 cf.cbSize = sizeof(CHARFORMAT);
\r
6928 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
6929 if (consoleEcho) {
\r
6930 cf.crTextColor = textAttribs[ColorNormal].color;
\r
6932 cf.crTextColor = COLOR_ECHOOFF;
\r
6934 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
6935 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
6937 case '\t': /* Tab key */
\r
6938 if (GetKeyState(VK_SHIFT) < 0) {
\r
6940 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
6943 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6944 if (buttonDesc[0].hwnd) {
\r
6945 SetFocus(buttonDesc[0].hwnd);
\r
6947 SetFocus(hwndMain);
\r
6951 case '\023': /* Ctrl+S */
\r
6952 sendNextChar = TRUE;
\r
6954 case '\021': /* Ctrl+Q */
\r
6955 quoteNextChar = TRUE;
\r
6965 GetWindowText(hwnd, buf, MSG_SIZ);
\r
6966 p = PrevInHistory(buf);
\r
6968 SetWindowText(hwnd, p);
\r
6969 sel.cpMin = 999999;
\r
6970 sel.cpMax = 999999;
\r
6971 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6976 p = NextInHistory();
\r
6978 SetWindowText(hwnd, p);
\r
6979 sel.cpMin = 999999;
\r
6980 sel.cpMax = 999999;
\r
6981 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6987 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6991 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
6995 case WM_MBUTTONDOWN:
\r
6996 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6997 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6999 case WM_RBUTTONUP:
\r
7000 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7001 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7002 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7006 hmenu = LoadMenu(hInst, "InputMenu");
\r
7007 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7008 if (sel.cpMin == sel.cpMax) {
\r
7009 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7010 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7012 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7013 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7015 pt.x = LOWORD(lParam);
\r
7016 pt.y = HIWORD(lParam);
\r
7017 MenuPopup(hwnd, pt, hmenu, -1);
\r
7021 switch (LOWORD(wParam)) {
\r
7023 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7025 case IDM_SelectAll:
\r
7027 sel.cpMax = -1; /*999999?*/
\r
7028 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7031 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7034 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7037 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7042 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7045 #define CO_MAX 100000
\r
7046 #define CO_TRIM 1000
\r
7049 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7051 static SnapData sd;
\r
7052 HWND hText, hInput;
\r
7054 static int sizeX, sizeY;
\r
7055 int newSizeX, newSizeY;
\r
7059 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7060 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7062 switch (message) {
\r
7064 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7066 ENLINK *pLink = (ENLINK*)lParam;
\r
7067 if (pLink->msg == WM_LBUTTONUP)
\r
7071 tr.chrg = pLink->chrg;
\r
7072 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7073 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7074 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7075 free(tr.lpstrText);
\r
7079 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7080 hwndConsole = hDlg;
\r
7082 consoleTextWindowProc = (WNDPROC)
\r
7083 SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);
\r
7084 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7085 consoleInputWindowProc = (WNDPROC)
\r
7086 SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);
\r
7087 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7088 Colorize(ColorNormal, TRUE);
\r
7089 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7090 ChangedConsoleFont();
\r
7091 GetClientRect(hDlg, &rect);
\r
7092 sizeX = rect.right;
\r
7093 sizeY = rect.bottom;
\r
7094 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7095 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7096 WINDOWPLACEMENT wp;
\r
7097 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7098 wp.length = sizeof(WINDOWPLACEMENT);
\r
7100 wp.showCmd = SW_SHOW;
\r
7101 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7102 wp.rcNormalPosition.left = wpConsole.x;
\r
7103 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7104 wp.rcNormalPosition.top = wpConsole.y;
\r
7105 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7106 SetWindowPlacement(hDlg, &wp);
\r
7109 // [HGM] Chessknight's change 2004-07-13
\r
7110 else { /* Determine Defaults */
\r
7111 WINDOWPLACEMENT wp;
\r
7112 wpConsole.x = wpMain.width + 1;
\r
7113 wpConsole.y = wpMain.y;
\r
7114 wpConsole.width = screenWidth - wpMain.width;
\r
7115 wpConsole.height = wpMain.height;
\r
7116 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7117 wp.length = sizeof(WINDOWPLACEMENT);
\r
7119 wp.showCmd = SW_SHOW;
\r
7120 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7121 wp.rcNormalPosition.left = wpConsole.x;
\r
7122 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7123 wp.rcNormalPosition.top = wpConsole.y;
\r
7124 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7125 SetWindowPlacement(hDlg, &wp);
\r
7128 // Allow hText to highlight URLs and send notifications on them
\r
7129 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7130 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7131 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7132 SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width
\r
7146 if (IsIconic(hDlg)) break;
\r
7147 newSizeX = LOWORD(lParam);
\r
7148 newSizeY = HIWORD(lParam);
\r
7149 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7150 RECT rectText, rectInput;
\r
7152 int newTextHeight, newTextWidth;
\r
7153 GetWindowRect(hText, &rectText);
\r
7154 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7155 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7156 if (newTextHeight < 0) {
\r
7157 newSizeY += -newTextHeight;
\r
7158 newTextHeight = 0;
\r
7160 SetWindowPos(hText, NULL, 0, 0,
\r
7161 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7162 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7163 pt.x = rectInput.left;
\r
7164 pt.y = rectInput.top + newSizeY - sizeY;
\r
7165 ScreenToClient(hDlg, &pt);
\r
7166 SetWindowPos(hInput, NULL,
\r
7167 pt.x, pt.y, /* needs client coords */
\r
7168 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7169 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7175 case WM_GETMINMAXINFO:
\r
7176 /* Prevent resizing window too small */
\r
7177 mmi = (MINMAXINFO *) lParam;
\r
7178 mmi->ptMinTrackSize.x = 100;
\r
7179 mmi->ptMinTrackSize.y = 100;
\r
7182 /* [AS] Snapping */
\r
7183 case WM_ENTERSIZEMOVE:
\r
7184 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7187 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7190 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7192 case WM_EXITSIZEMOVE:
\r
7193 UpdateICSWidth(hText);
\r
7194 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7197 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7205 if (hwndConsole) return;
\r
7206 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7207 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7212 ConsoleOutput(char* data, int length, int forceVisible)
\r
7217 char buf[CO_MAX+1];
\r
7220 static int delayLF = 0;
\r
7221 CHARRANGE savesel, sel;
\r
7223 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7231 while (length--) {
\r
7239 } else if (*p == '\007') {
\r
7240 MyPlaySound(&sounds[(int)SoundBell]);
\r
7247 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7248 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7249 /* Save current selection */
\r
7250 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7251 exlen = GetWindowTextLength(hText);
\r
7252 /* Find out whether current end of text is visible */
\r
7253 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7254 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7255 /* Trim existing text if it's too long */
\r
7256 if (exlen + (q - buf) > CO_MAX) {
\r
7257 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7260 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7261 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7263 savesel.cpMin -= trim;
\r
7264 savesel.cpMax -= trim;
\r
7265 if (exlen < 0) exlen = 0;
\r
7266 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7267 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7269 /* Append the new text */
\r
7270 sel.cpMin = exlen;
\r
7271 sel.cpMax = exlen;
\r
7272 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7273 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7274 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7275 if (forceVisible || exlen == 0 ||
\r
7276 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7277 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7278 /* Scroll to make new end of text visible if old end of text
\r
7279 was visible or new text is an echo of user typein */
\r
7280 sel.cpMin = 9999999;
\r
7281 sel.cpMax = 9999999;
\r
7282 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7283 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7284 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7285 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7287 if (savesel.cpMax == exlen || forceVisible) {
\r
7288 /* Move insert point to new end of text if it was at the old
\r
7289 end of text or if the new text is an echo of user typein */
\r
7290 sel.cpMin = 9999999;
\r
7291 sel.cpMax = 9999999;
\r
7292 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7294 /* Restore previous selection */
\r
7295 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7297 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7304 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7308 COLORREF oldFg, oldBg;
\r
7312 if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;
\r
7314 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7315 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7316 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7319 rect.right = x + squareSize;
\r
7321 rect.bottom = y + squareSize;
\r
7324 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7325 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7326 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7327 &rect, str, strlen(str), NULL);
\r
7329 (void) SetTextColor(hdc, oldFg);
\r
7330 (void) SetBkColor(hdc, oldBg);
\r
7331 (void) SelectObject(hdc, oldFont);
\r
7335 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7336 RECT *rect, char *color, char *flagFell)
\r
7340 COLORREF oldFg, oldBg;
\r
7343 if (appData.clockMode) {
\r
7345 sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7347 sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7354 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7355 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7357 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7358 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7360 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7364 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7365 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7366 rect, str, strlen(str), NULL);
\r
7367 if(logoHeight > 0 && appData.clockMode) {
\r
7369 sprintf(buf, "%s %s", buf+7, flagFell);
\r
7370 r.top = rect->top + logoHeight/2;
\r
7371 r.left = rect->left;
\r
7372 r.right = rect->right;
\r
7373 r.bottom = rect->bottom;
\r
7374 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7375 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7376 &r, str, strlen(str), NULL);
\r
7378 (void) SetTextColor(hdc, oldFg);
\r
7379 (void) SetBkColor(hdc, oldBg);
\r
7380 (void) SelectObject(hdc, oldFont);
\r
7385 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7391 if( count <= 0 ) {
\r
7392 if (appData.debugMode) {
\r
7393 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7396 return ERROR_INVALID_USER_BUFFER;
\r
7399 ResetEvent(ovl->hEvent);
\r
7400 ovl->Offset = ovl->OffsetHigh = 0;
\r
7401 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7405 err = GetLastError();
\r
7406 if (err == ERROR_IO_PENDING) {
\r
7407 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7411 err = GetLastError();
\r
7418 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7423 ResetEvent(ovl->hEvent);
\r
7424 ovl->Offset = ovl->OffsetHigh = 0;
\r
7425 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7429 err = GetLastError();
\r
7430 if (err == ERROR_IO_PENDING) {
\r
7431 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7435 err = GetLastError();
\r
7441 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7442 void CheckForInputBufferFull( InputSource * is )
\r
7444 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7445 /* Look for end of line */
\r
7446 char * p = is->buf;
\r
7448 while( p < is->next && *p != '\n' ) {
\r
7452 if( p >= is->next ) {
\r
7453 if (appData.debugMode) {
\r
7454 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7457 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7458 is->count = (DWORD) -1;
\r
7459 is->next = is->buf;
\r
7465 InputThread(LPVOID arg)
\r
7470 is = (InputSource *) arg;
\r
7471 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7472 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7473 while (is->hThread != NULL) {
\r
7474 is->error = DoReadFile(is->hFile, is->next,
\r
7475 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7476 &is->count, &ovl);
\r
7477 if (is->error == NO_ERROR) {
\r
7478 is->next += is->count;
\r
7480 if (is->error == ERROR_BROKEN_PIPE) {
\r
7481 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7484 is->count = (DWORD) -1;
\r
7485 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7490 CheckForInputBufferFull( is );
\r
7492 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7494 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7496 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7499 CloseHandle(ovl.hEvent);
\r
7500 CloseHandle(is->hFile);
\r
7502 if (appData.debugMode) {
\r
7503 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7510 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7512 NonOvlInputThread(LPVOID arg)
\r
7519 is = (InputSource *) arg;
\r
7520 while (is->hThread != NULL) {
\r
7521 is->error = ReadFile(is->hFile, is->next,
\r
7522 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7523 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7524 if (is->error == NO_ERROR) {
\r
7525 /* Change CRLF to LF */
\r
7526 if (is->next > is->buf) {
\r
7528 i = is->count + 1;
\r
7536 if (prev == '\r' && *p == '\n') {
\r
7548 if (is->error == ERROR_BROKEN_PIPE) {
\r
7549 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7552 is->count = (DWORD) -1;
\r
7556 CheckForInputBufferFull( is );
\r
7558 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7560 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7562 if (is->count < 0) break; /* Quit on error */
\r
7564 CloseHandle(is->hFile);
\r
7569 SocketInputThread(LPVOID arg)
\r
7573 is = (InputSource *) arg;
\r
7574 while (is->hThread != NULL) {
\r
7575 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7576 if ((int)is->count == SOCKET_ERROR) {
\r
7577 is->count = (DWORD) -1;
\r
7578 is->error = WSAGetLastError();
\r
7580 is->error = NO_ERROR;
\r
7581 is->next += is->count;
\r
7582 if (is->count == 0 && is->second == is) {
\r
7583 /* End of file on stderr; quit with no message */
\r
7587 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7589 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7591 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7597 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7601 is = (InputSource *) lParam;
\r
7602 if (is->lineByLine) {
\r
7603 /* Feed in lines one by one */
\r
7604 char *p = is->buf;
\r
7606 while (q < is->next) {
\r
7607 if (*q++ == '\n') {
\r
7608 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7613 /* Move any partial line to the start of the buffer */
\r
7615 while (p < is->next) {
\r
7620 if (is->error != NO_ERROR || is->count == 0) {
\r
7621 /* Notify backend of the error. Note: If there was a partial
\r
7622 line at the end, it is not flushed through. */
\r
7623 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7626 /* Feed in the whole chunk of input at once */
\r
7627 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7628 is->next = is->buf;
\r
7632 /*---------------------------------------------------------------------------*\
\r
7634 * Menu enables. Used when setting various modes.
\r
7636 \*---------------------------------------------------------------------------*/
\r
7644 GreyRevert(Boolean grey)
\r
7645 { // [HGM] vari: for retracting variations in local mode
\r
7646 HMENU hmenu = GetMenu(hwndMain);
\r
7647 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7648 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7652 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7654 while (enab->item > 0) {
\r
7655 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7660 Enables gnuEnables[] = {
\r
7661 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7662 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7663 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7664 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7665 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7666 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7667 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7668 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7669 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7670 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7671 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7672 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7673 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7677 Enables icsEnables[] = {
\r
7678 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7679 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7680 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7681 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7682 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7683 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7684 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7685 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7686 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7687 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7688 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7689 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7690 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7691 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7692 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7693 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7694 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7699 Enables zippyEnables[] = {
\r
7700 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7701 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7702 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7703 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7708 Enables ncpEnables[] = {
\r
7709 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7710 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7711 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7712 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7713 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7714 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7715 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7716 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7717 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7718 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7719 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7720 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7721 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7722 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7723 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7724 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7725 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7726 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7727 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7728 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7729 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7733 Enables trainingOnEnables[] = {
\r
7734 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7735 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7736 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7737 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7738 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7739 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7740 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7741 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7745 Enables trainingOffEnables[] = {
\r
7746 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7747 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7748 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7749 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7750 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7751 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7752 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7753 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7757 /* These modify either ncpEnables or gnuEnables */
\r
7758 Enables cmailEnables[] = {
\r
7759 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7760 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7761 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7762 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7763 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7764 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7765 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7769 Enables machineThinkingEnables[] = {
\r
7770 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7771 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7772 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7773 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7774 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7775 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7776 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7777 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7778 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7779 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7780 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7781 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7782 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7783 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7784 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7785 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7789 Enables userThinkingEnables[] = {
\r
7790 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7791 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7792 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7793 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7794 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7795 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7796 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7797 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7798 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7799 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7800 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7801 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7802 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7803 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7804 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7805 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7809 /*---------------------------------------------------------------------------*\
\r
7811 * Front-end interface functions exported by XBoard.
\r
7812 * Functions appear in same order as prototypes in frontend.h.
\r
7814 \*---------------------------------------------------------------------------*/
\r
7818 static UINT prevChecked = 0;
\r
7819 static int prevPausing = 0;
\r
7822 if (pausing != prevPausing) {
\r
7823 prevPausing = pausing;
\r
7824 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7825 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7826 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7829 switch (gameMode) {
\r
7830 case BeginningOfGame:
\r
7831 if (appData.icsActive)
\r
7832 nowChecked = IDM_IcsClient;
\r
7833 else if (appData.noChessProgram)
\r
7834 nowChecked = IDM_EditGame;
\r
7836 nowChecked = IDM_MachineBlack;
\r
7838 case MachinePlaysBlack:
\r
7839 nowChecked = IDM_MachineBlack;
\r
7841 case MachinePlaysWhite:
\r
7842 nowChecked = IDM_MachineWhite;
\r
7844 case TwoMachinesPlay:
\r
7845 nowChecked = matchMode ? IDM_Match : IDM_TwoMachines; // [HGM] match
\r
7848 nowChecked = IDM_AnalysisMode;
\r
7851 nowChecked = IDM_AnalyzeFile;
\r
7854 nowChecked = IDM_EditGame;
\r
7856 case PlayFromGameFile:
\r
7857 nowChecked = IDM_LoadGame;
\r
7859 case EditPosition:
\r
7860 nowChecked = IDM_EditPosition;
\r
7863 nowChecked = IDM_Training;
\r
7865 case IcsPlayingWhite:
\r
7866 case IcsPlayingBlack:
\r
7867 case IcsObserving:
\r
7869 nowChecked = IDM_IcsClient;
\r
7876 if (prevChecked != 0)
\r
7877 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7878 prevChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
7879 if (nowChecked != 0)
\r
7880 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7881 nowChecked, MF_BYCOMMAND|MF_CHECKED);
\r
7883 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
7884 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
7885 MF_BYCOMMAND|MF_ENABLED);
\r
7887 (void) EnableMenuItem(GetMenu(hwndMain),
\r
7888 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
7891 prevChecked = nowChecked;
\r
7893 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
7894 if (appData.icsActive) {
\r
7895 if (appData.icsEngineAnalyze) {
\r
7896 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7897 MF_BYCOMMAND|MF_CHECKED);
\r
7899 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7900 MF_BYCOMMAND|MF_UNCHECKED);
\r
7908 HMENU hmenu = GetMenu(hwndMain);
\r
7909 SetMenuEnables(hmenu, icsEnables);
\r
7910 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,
\r
7911 MF_BYPOSITION|MF_ENABLED);
\r
7913 if (appData.zippyPlay) {
\r
7914 SetMenuEnables(hmenu, zippyEnables);
\r
7915 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
7916 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7917 MF_BYCOMMAND|MF_ENABLED);
\r
7925 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
7931 HMENU hmenu = GetMenu(hwndMain);
\r
7932 SetMenuEnables(hmenu, ncpEnables);
\r
7933 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,
\r
7934 MF_BYPOSITION|MF_GRAYED);
\r
7935 DrawMenuBar(hwndMain);
\r
7941 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
7945 SetTrainingModeOn()
\r
7948 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
7949 for (i = 0; i < N_BUTTONS; i++) {
\r
7950 if (buttonDesc[i].hwnd != NULL)
\r
7951 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
7956 VOID SetTrainingModeOff()
\r
7959 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
7960 for (i = 0; i < N_BUTTONS; i++) {
\r
7961 if (buttonDesc[i].hwnd != NULL)
\r
7962 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
7968 SetUserThinkingEnables()
\r
7970 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
7974 SetMachineThinkingEnables()
\r
7976 HMENU hMenu = GetMenu(hwndMain);
\r
7977 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
7979 SetMenuEnables(hMenu, machineThinkingEnables);
\r
7981 if (gameMode == MachinePlaysBlack) {
\r
7982 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
7983 } else if (gameMode == MachinePlaysWhite) {
\r
7984 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
7985 } else if (gameMode == TwoMachinesPlay) {
\r
7986 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
7992 DisplayTitle(char *str)
\r
7994 char title[MSG_SIZ], *host;
\r
7995 if (str[0] != NULLCHAR) {
\r
7996 strcpy(title, str);
\r
7997 } else if (appData.icsActive) {
\r
7998 if (appData.icsCommPort[0] != NULLCHAR)
\r
8001 host = appData.icsHost;
\r
8002 sprintf(title, "%s: %s", szTitle, host);
\r
8003 } else if (appData.noChessProgram) {
\r
8004 strcpy(title, szTitle);
\r
8006 strcpy(title, szTitle);
\r
8007 strcat(title, ": ");
\r
8008 strcat(title, first.tidy);
\r
8010 SetWindowText(hwndMain, title);
\r
8015 DisplayMessage(char *str1, char *str2)
\r
8019 int remain = MESSAGE_TEXT_MAX - 1;
\r
8022 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8023 messageText[0] = NULLCHAR;
\r
8025 len = strlen(str1);
\r
8026 if (len > remain) len = remain;
\r
8027 strncpy(messageText, str1, len);
\r
8028 messageText[len] = NULLCHAR;
\r
8031 if (*str2 && remain >= 2) {
\r
8033 strcat(messageText, " ");
\r
8036 len = strlen(str2);
\r
8037 if (len > remain) len = remain;
\r
8038 strncat(messageText, str2, len);
\r
8040 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8042 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8046 hdc = GetDC(hwndMain);
\r
8047 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8048 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8049 &messageRect, messageText, strlen(messageText), NULL);
\r
8050 (void) SelectObject(hdc, oldFont);
\r
8051 (void) ReleaseDC(hwndMain, hdc);
\r
8055 DisplayError(char *str, int error)
\r
8057 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8063 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8064 NULL, error, LANG_NEUTRAL,
\r
8065 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8067 sprintf(buf, "%s:\n%s", str, buf2);
\r
8069 ErrorMap *em = errmap;
\r
8070 while (em->err != 0 && em->err != error) em++;
\r
8071 if (em->err != 0) {
\r
8072 sprintf(buf, "%s:\n%s", str, em->msg);
\r
8074 sprintf(buf, "%s:\nError code %d", str, error);
\r
8079 ErrorPopUp(_("Error"), buf);
\r
8084 DisplayMoveError(char *str)
\r
8086 fromX = fromY = -1;
\r
8087 ClearHighlights();
\r
8088 DrawPosition(FALSE, NULL);
\r
8089 if (appData.popupMoveErrors) {
\r
8090 ErrorPopUp(_("Error"), str);
\r
8092 DisplayMessage(str, "");
\r
8093 moveErrorMessageUp = TRUE;
\r
8098 DisplayFatalError(char *str, int error, int exitStatus)
\r
8100 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8102 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8105 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8106 NULL, error, LANG_NEUTRAL,
\r
8107 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8109 sprintf(buf, "%s:\n%s", str, buf2);
\r
8111 ErrorMap *em = errmap;
\r
8112 while (em->err != 0 && em->err != error) em++;
\r
8113 if (em->err != 0) {
\r
8114 sprintf(buf, "%s:\n%s", str, em->msg);
\r
8116 sprintf(buf, "%s:\nError code %d", str, error);
\r
8121 if (appData.debugMode) {
\r
8122 fprintf(debugFP, "%s: %s\n", label, str);
\r
8124 if (appData.popupExitMessage) {
\r
8125 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8126 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8128 ExitEvent(exitStatus);
\r
8133 DisplayInformation(char *str)
\r
8135 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8140 DisplayNote(char *str)
\r
8142 ErrorPopUp(_("Note"), str);
\r
8147 char *title, *question, *replyPrefix;
\r
8152 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8154 static QuestionParams *qp;
\r
8155 char reply[MSG_SIZ];
\r
8158 switch (message) {
\r
8159 case WM_INITDIALOG:
\r
8160 qp = (QuestionParams *) lParam;
\r
8161 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8162 Translate(hDlg, DLG_Question);
\r
8163 SetWindowText(hDlg, qp->title);
\r
8164 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8165 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8169 switch (LOWORD(wParam)) {
\r
8171 strcpy(reply, qp->replyPrefix);
\r
8172 if (*reply) strcat(reply, " ");
\r
8173 len = strlen(reply);
\r
8174 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8175 strcat(reply, "\n");
\r
8176 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8177 EndDialog(hDlg, TRUE);
\r
8178 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8181 EndDialog(hDlg, FALSE);
\r
8192 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8194 QuestionParams qp;
\r
8198 qp.question = question;
\r
8199 qp.replyPrefix = replyPrefix;
\r
8201 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8202 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8203 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8204 FreeProcInstance(lpProc);
\r
8207 /* [AS] Pick FRC position */
\r
8208 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8210 static int * lpIndexFRC;
\r
8216 case WM_INITDIALOG:
\r
8217 lpIndexFRC = (int *) lParam;
\r
8219 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8220 Translate(hDlg, DLG_NewGameFRC);
\r
8222 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8223 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8224 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8225 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8230 switch( LOWORD(wParam) ) {
\r
8232 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8233 EndDialog( hDlg, 0 );
\r
8234 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8237 EndDialog( hDlg, 1 );
\r
8239 case IDC_NFG_Edit:
\r
8240 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8241 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8243 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8246 case IDC_NFG_Random:
\r
8247 sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8248 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8261 int index = appData.defaultFrcPosition;
\r
8262 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8264 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8266 if( result == 0 ) {
\r
8267 appData.defaultFrcPosition = index;
\r
8273 /* [AS] Game list options. Refactored by HGM */
\r
8275 HWND gameListOptionsDialog;
\r
8277 // low-level front-end: clear text edit / list widget
\r
8281 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8284 // low-level front-end: clear text edit / list widget
\r
8286 GLT_DeSelectList()
\r
8288 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8291 // low-level front-end: append line to text edit / list widget
\r
8293 GLT_AddToList( char *name )
\r
8296 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8300 // low-level front-end: get line from text edit / list widget
\r
8302 GLT_GetFromList( int index, char *name )
\r
8305 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8311 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8313 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8314 int idx2 = idx1 + delta;
\r
8315 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8317 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8320 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8321 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8322 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8323 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8327 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8331 case WM_INITDIALOG:
\r
8332 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8334 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8335 Translate(hDlg, DLG_GameListOptions);
\r
8337 /* Initialize list */
\r
8338 GLT_TagsToList( lpUserGLT );
\r
8340 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8345 switch( LOWORD(wParam) ) {
\r
8348 EndDialog( hDlg, 0 );
\r
8351 EndDialog( hDlg, 1 );
\r
8354 case IDC_GLT_Default:
\r
8355 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8358 case IDC_GLT_Restore:
\r
8359 GLT_TagsToList( appData.gameListTags );
\r
8363 GLT_MoveSelection( hDlg, -1 );
\r
8366 case IDC_GLT_Down:
\r
8367 GLT_MoveSelection( hDlg, +1 );
\r
8377 int GameListOptions()
\r
8380 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8382 strcpy( lpUserGLT, appData.gameListTags );
\r
8384 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8386 if( result == 0 ) {
\r
8387 /* [AS] Memory leak here! */
\r
8388 appData.gameListTags = strdup( lpUserGLT );
\r
8395 DisplayIcsInteractionTitle(char *str)
\r
8397 char consoleTitle[MSG_SIZ];
\r
8399 sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);
\r
8400 SetWindowText(hwndConsole, consoleTitle);
\r
8404 DrawPosition(int fullRedraw, Board board)
\r
8406 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8409 void NotifyFrontendLogin()
\r
8412 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8418 fromX = fromY = -1;
\r
8419 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8420 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8421 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8422 dragInfo.lastpos = dragInfo.pos;
\r
8423 dragInfo.start.x = dragInfo.start.y = -1;
\r
8424 dragInfo.from = dragInfo.start;
\r
8426 DrawPosition(TRUE, NULL);
\r
8433 CommentPopUp(char *title, char *str)
\r
8435 HWND hwnd = GetActiveWindow();
\r
8436 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8438 SetActiveWindow(hwnd);
\r
8442 CommentPopDown(void)
\r
8444 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);
\r
8445 if (commentDialog) {
\r
8446 ShowWindow(commentDialog, SW_HIDE);
\r
8448 commentUp = FALSE;
\r
8452 EditCommentPopUp(int index, char *title, char *str)
\r
8454 EitherCommentPopUp(index, title, str, TRUE);
\r
8461 MyPlaySound(&sounds[(int)SoundMove]);
\r
8464 VOID PlayIcsWinSound()
\r
8466 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8469 VOID PlayIcsLossSound()
\r
8471 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8474 VOID PlayIcsDrawSound()
\r
8476 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8479 VOID PlayIcsUnfinishedSound()
\r
8481 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8487 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8495 consoleEcho = TRUE;
\r
8496 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8497 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8498 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8507 consoleEcho = FALSE;
\r
8508 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8509 /* This works OK: set text and background both to the same color */
\r
8511 cf.crTextColor = COLOR_ECHOOFF;
\r
8512 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8513 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8516 /* No Raw()...? */
\r
8518 void Colorize(ColorClass cc, int continuation)
\r
8520 currentColorClass = cc;
\r
8521 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8522 consoleCF.crTextColor = textAttribs[cc].color;
\r
8523 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8524 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8530 static char buf[MSG_SIZ];
\r
8531 DWORD bufsiz = MSG_SIZ;
\r
8533 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8534 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8536 if (!GetUserName(buf, &bufsiz)) {
\r
8537 /*DisplayError("Error getting user name", GetLastError());*/
\r
8538 strcpy(buf, _("User"));
\r
8546 static char buf[MSG_SIZ];
\r
8547 DWORD bufsiz = MSG_SIZ;
\r
8549 if (!GetComputerName(buf, &bufsiz)) {
\r
8550 /*DisplayError("Error getting host name", GetLastError());*/
\r
8551 strcpy(buf, _("Unknown"));
\r
8558 ClockTimerRunning()
\r
8560 return clockTimerEvent != 0;
\r
8566 if (clockTimerEvent == 0) return FALSE;
\r
8567 KillTimer(hwndMain, clockTimerEvent);
\r
8568 clockTimerEvent = 0;
\r
8573 StartClockTimer(long millisec)
\r
8575 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8576 (UINT) millisec, NULL);
\r
8580 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8583 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8585 if(appData.noGUI) return;
\r
8586 hdc = GetDC(hwndMain);
\r
8587 if (!IsIconic(hwndMain)) {
\r
8588 DisplayAClock(hdc, timeRemaining, highlight,
\r
8589 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8591 if (highlight && iconCurrent == iconBlack) {
\r
8592 iconCurrent = iconWhite;
\r
8593 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8594 if (IsIconic(hwndMain)) {
\r
8595 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8598 (void) ReleaseDC(hwndMain, hdc);
\r
8600 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8604 DisplayBlackClock(long timeRemaining, int highlight)
\r
8607 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8609 if(appData.noGUI) return;
\r
8610 hdc = GetDC(hwndMain);
\r
8611 if (!IsIconic(hwndMain)) {
\r
8612 DisplayAClock(hdc, timeRemaining, highlight,
\r
8613 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8615 if (highlight && iconCurrent == iconWhite) {
\r
8616 iconCurrent = iconBlack;
\r
8617 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8618 if (IsIconic(hwndMain)) {
\r
8619 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8622 (void) ReleaseDC(hwndMain, hdc);
\r
8624 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8629 LoadGameTimerRunning()
\r
8631 return loadGameTimerEvent != 0;
\r
8635 StopLoadGameTimer()
\r
8637 if (loadGameTimerEvent == 0) return FALSE;
\r
8638 KillTimer(hwndMain, loadGameTimerEvent);
\r
8639 loadGameTimerEvent = 0;
\r
8644 StartLoadGameTimer(long millisec)
\r
8646 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8647 (UINT) millisec, NULL);
\r
8655 char fileTitle[MSG_SIZ];
\r
8657 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8658 f = OpenFileDialog(hwndMain, "a", defName,
\r
8659 appData.oldSaveStyle ? "gam" : "pgn",
\r
8661 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8663 SaveGame(f, 0, "");
\r
8670 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8672 if (delayedTimerEvent != 0) {
\r
8673 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8674 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8676 KillTimer(hwndMain, delayedTimerEvent);
\r
8677 delayedTimerEvent = 0;
\r
8678 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8679 delayedTimerCallback();
\r
8681 delayedTimerCallback = cb;
\r
8682 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8683 (UINT) millisec, NULL);
\r
8686 DelayedEventCallback
\r
8689 if (delayedTimerEvent) {
\r
8690 return delayedTimerCallback;
\r
8697 CancelDelayedEvent()
\r
8699 if (delayedTimerEvent) {
\r
8700 KillTimer(hwndMain, delayedTimerEvent);
\r
8701 delayedTimerEvent = 0;
\r
8705 DWORD GetWin32Priority(int nice)
\r
8706 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8708 REALTIME_PRIORITY_CLASS 0x00000100
\r
8709 HIGH_PRIORITY_CLASS 0x00000080
\r
8710 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8711 NORMAL_PRIORITY_CLASS 0x00000020
\r
8712 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8713 IDLE_PRIORITY_CLASS 0x00000040
\r
8715 if (nice < -15) return 0x00000080;
\r
8716 if (nice < 0) return 0x00008000;
\r
8717 if (nice == 0) return 0x00000020;
\r
8718 if (nice < 15) return 0x00004000;
\r
8719 return 0x00000040;
\r
8722 /* Start a child process running the given program.
\r
8723 The process's standard output can be read from "from", and its
\r
8724 standard input can be written to "to".
\r
8725 Exit with fatal error if anything goes wrong.
\r
8726 Returns an opaque pointer that can be used to destroy the process
\r
8730 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8732 #define BUFSIZE 4096
\r
8734 HANDLE hChildStdinRd, hChildStdinWr,
\r
8735 hChildStdoutRd, hChildStdoutWr;
\r
8736 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8737 SECURITY_ATTRIBUTES saAttr;
\r
8739 PROCESS_INFORMATION piProcInfo;
\r
8740 STARTUPINFO siStartInfo;
\r
8742 char buf[MSG_SIZ];
\r
8745 if (appData.debugMode) {
\r
8746 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8751 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8752 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8753 saAttr.bInheritHandle = TRUE;
\r
8754 saAttr.lpSecurityDescriptor = NULL;
\r
8757 * The steps for redirecting child's STDOUT:
\r
8758 * 1. Create anonymous pipe to be STDOUT for child.
\r
8759 * 2. Create a noninheritable duplicate of read handle,
\r
8760 * and close the inheritable read handle.
\r
8763 /* Create a pipe for the child's STDOUT. */
\r
8764 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8765 return GetLastError();
\r
8768 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8769 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8770 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8771 FALSE, /* not inherited */
\r
8772 DUPLICATE_SAME_ACCESS);
\r
8774 return GetLastError();
\r
8776 CloseHandle(hChildStdoutRd);
\r
8779 * The steps for redirecting child's STDIN:
\r
8780 * 1. Create anonymous pipe to be STDIN for child.
\r
8781 * 2. Create a noninheritable duplicate of write handle,
\r
8782 * and close the inheritable write handle.
\r
8785 /* Create a pipe for the child's STDIN. */
\r
8786 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8787 return GetLastError();
\r
8790 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8791 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8792 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8793 FALSE, /* not inherited */
\r
8794 DUPLICATE_SAME_ACCESS);
\r
8796 return GetLastError();
\r
8798 CloseHandle(hChildStdinWr);
\r
8800 /* Arrange to (1) look in dir for the child .exe file, and
\r
8801 * (2) have dir be the child's working directory. Interpret
\r
8802 * dir relative to the directory WinBoard loaded from. */
\r
8803 GetCurrentDirectory(MSG_SIZ, buf);
\r
8804 SetCurrentDirectory(installDir);
\r
8805 SetCurrentDirectory(dir);
\r
8807 /* Now create the child process. */
\r
8809 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8810 siStartInfo.lpReserved = NULL;
\r
8811 siStartInfo.lpDesktop = NULL;
\r
8812 siStartInfo.lpTitle = NULL;
\r
8813 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8814 siStartInfo.cbReserved2 = 0;
\r
8815 siStartInfo.lpReserved2 = NULL;
\r
8816 siStartInfo.hStdInput = hChildStdinRd;
\r
8817 siStartInfo.hStdOutput = hChildStdoutWr;
\r
8818 siStartInfo.hStdError = hChildStdoutWr;
\r
8820 fSuccess = CreateProcess(NULL,
\r
8821 cmdLine, /* command line */
\r
8822 NULL, /* process security attributes */
\r
8823 NULL, /* primary thread security attrs */
\r
8824 TRUE, /* handles are inherited */
\r
8825 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8826 NULL, /* use parent's environment */
\r
8828 &siStartInfo, /* STARTUPINFO pointer */
\r
8829 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8831 err = GetLastError();
\r
8832 SetCurrentDirectory(buf); /* return to prev directory */
\r
8837 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
8838 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
8839 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
8842 /* Close the handles we don't need in the parent */
\r
8843 CloseHandle(piProcInfo.hThread);
\r
8844 CloseHandle(hChildStdinRd);
\r
8845 CloseHandle(hChildStdoutWr);
\r
8847 /* Prepare return value */
\r
8848 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8849 cp->kind = CPReal;
\r
8850 cp->hProcess = piProcInfo.hProcess;
\r
8851 cp->pid = piProcInfo.dwProcessId;
\r
8852 cp->hFrom = hChildStdoutRdDup;
\r
8853 cp->hTo = hChildStdinWrDup;
\r
8855 *pr = (void *) cp;
\r
8857 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
8858 2000 where engines sometimes don't see the initial command(s)
\r
8859 from WinBoard and hang. I don't understand how that can happen,
\r
8860 but the Sleep is harmless, so I've put it in. Others have also
\r
8861 reported what may be the same problem, so hopefully this will fix
\r
8862 it for them too. */
\r
8870 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
8872 ChildProc *cp; int result;
\r
8874 cp = (ChildProc *) pr;
\r
8875 if (cp == NULL) return;
\r
8877 switch (cp->kind) {
\r
8879 /* TerminateProcess is considered harmful, so... */
\r
8880 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
8881 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
8882 /* The following doesn't work because the chess program
\r
8883 doesn't "have the same console" as WinBoard. Maybe
\r
8884 we could arrange for this even though neither WinBoard
\r
8885 nor the chess program uses a console for stdio? */
\r
8886 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
8888 /* [AS] Special termination modes for misbehaving programs... */
\r
8889 if( signal == 9 ) {
\r
8890 result = TerminateProcess( cp->hProcess, 0 );
\r
8892 if ( appData.debugMode) {
\r
8893 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
8896 else if( signal == 10 ) {
\r
8897 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
8899 if( dw != WAIT_OBJECT_0 ) {
\r
8900 result = TerminateProcess( cp->hProcess, 0 );
\r
8902 if ( appData.debugMode) {
\r
8903 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
8909 CloseHandle(cp->hProcess);
\r
8913 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
8917 closesocket(cp->sock);
\r
8922 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
8923 closesocket(cp->sock);
\r
8924 closesocket(cp->sock2);
\r
8932 InterruptChildProcess(ProcRef pr)
\r
8936 cp = (ChildProc *) pr;
\r
8937 if (cp == NULL) return;
\r
8938 switch (cp->kind) {
\r
8940 /* The following doesn't work because the chess program
\r
8941 doesn't "have the same console" as WinBoard. Maybe
\r
8942 we could arrange for this even though neither WinBoard
\r
8943 nor the chess program uses a console for stdio */
\r
8944 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
8949 /* Can't interrupt */
\r
8953 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
8960 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
8962 char cmdLine[MSG_SIZ];
\r
8964 if (port[0] == NULLCHAR) {
\r
8965 sprintf(cmdLine, "%s %s", appData.telnetProgram, host);
\r
8967 sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);
\r
8969 return StartChildProcess(cmdLine, "", pr);
\r
8973 /* Code to open TCP sockets */
\r
8976 OpenTCP(char *host, char *port, ProcRef *pr)
\r
8981 struct sockaddr_in sa, mysa;
\r
8982 struct hostent FAR *hp;
\r
8983 unsigned short uport;
\r
8984 WORD wVersionRequested;
\r
8987 /* Initialize socket DLL */
\r
8988 wVersionRequested = MAKEWORD(1, 1);
\r
8989 err = WSAStartup(wVersionRequested, &wsaData);
\r
8990 if (err != 0) return err;
\r
8993 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8994 err = WSAGetLastError();
\r
8999 /* Bind local address using (mostly) don't-care values.
\r
9001 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9002 mysa.sin_family = AF_INET;
\r
9003 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9004 uport = (unsigned short) 0;
\r
9005 mysa.sin_port = htons(uport);
\r
9006 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9007 == SOCKET_ERROR) {
\r
9008 err = WSAGetLastError();
\r
9013 /* Resolve remote host name */
\r
9014 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9015 if (!(hp = gethostbyname(host))) {
\r
9016 unsigned int b0, b1, b2, b3;
\r
9018 err = WSAGetLastError();
\r
9020 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9021 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9022 hp->h_addrtype = AF_INET;
\r
9024 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9025 hp->h_addr_list[0] = (char *) malloc(4);
\r
9026 hp->h_addr_list[0][0] = (char) b0;
\r
9027 hp->h_addr_list[0][1] = (char) b1;
\r
9028 hp->h_addr_list[0][2] = (char) b2;
\r
9029 hp->h_addr_list[0][3] = (char) b3;
\r
9035 sa.sin_family = hp->h_addrtype;
\r
9036 uport = (unsigned short) atoi(port);
\r
9037 sa.sin_port = htons(uport);
\r
9038 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9040 /* Make connection */
\r
9041 if (connect(s, (struct sockaddr *) &sa,
\r
9042 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9043 err = WSAGetLastError();
\r
9048 /* Prepare return value */
\r
9049 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9050 cp->kind = CPSock;
\r
9052 *pr = (ProcRef *) cp;
\r
9058 OpenCommPort(char *name, ProcRef *pr)
\r
9063 char fullname[MSG_SIZ];
\r
9065 if (*name != '\\')
\r
9066 sprintf(fullname, "\\\\.\\%s", name);
\r
9068 strcpy(fullname, name);
\r
9070 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9071 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9072 if (h == (HANDLE) -1) {
\r
9073 return GetLastError();
\r
9077 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9079 /* Accumulate characters until a 100ms pause, then parse */
\r
9080 ct.ReadIntervalTimeout = 100;
\r
9081 ct.ReadTotalTimeoutMultiplier = 0;
\r
9082 ct.ReadTotalTimeoutConstant = 0;
\r
9083 ct.WriteTotalTimeoutMultiplier = 0;
\r
9084 ct.WriteTotalTimeoutConstant = 0;
\r
9085 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9087 /* Prepare return value */
\r
9088 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9089 cp->kind = CPComm;
\r
9092 *pr = (ProcRef *) cp;
\r
9098 OpenLoopback(ProcRef *pr)
\r
9100 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9106 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9111 struct sockaddr_in sa, mysa;
\r
9112 struct hostent FAR *hp;
\r
9113 unsigned short uport;
\r
9114 WORD wVersionRequested;
\r
9117 char stderrPortStr[MSG_SIZ];
\r
9119 /* Initialize socket DLL */
\r
9120 wVersionRequested = MAKEWORD(1, 1);
\r
9121 err = WSAStartup(wVersionRequested, &wsaData);
\r
9122 if (err != 0) return err;
\r
9124 /* Resolve remote host name */
\r
9125 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9126 if (!(hp = gethostbyname(host))) {
\r
9127 unsigned int b0, b1, b2, b3;
\r
9129 err = WSAGetLastError();
\r
9131 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9132 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9133 hp->h_addrtype = AF_INET;
\r
9135 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9136 hp->h_addr_list[0] = (char *) malloc(4);
\r
9137 hp->h_addr_list[0][0] = (char) b0;
\r
9138 hp->h_addr_list[0][1] = (char) b1;
\r
9139 hp->h_addr_list[0][2] = (char) b2;
\r
9140 hp->h_addr_list[0][3] = (char) b3;
\r
9146 sa.sin_family = hp->h_addrtype;
\r
9147 uport = (unsigned short) 514;
\r
9148 sa.sin_port = htons(uport);
\r
9149 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9151 /* Bind local socket to unused "privileged" port address
\r
9153 s = INVALID_SOCKET;
\r
9154 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9155 mysa.sin_family = AF_INET;
\r
9156 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9157 for (fromPort = 1023;; fromPort--) {
\r
9158 if (fromPort < 0) {
\r
9160 return WSAEADDRINUSE;
\r
9162 if (s == INVALID_SOCKET) {
\r
9163 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9164 err = WSAGetLastError();
\r
9169 uport = (unsigned short) fromPort;
\r
9170 mysa.sin_port = htons(uport);
\r
9171 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9172 == SOCKET_ERROR) {
\r
9173 err = WSAGetLastError();
\r
9174 if (err == WSAEADDRINUSE) continue;
\r
9178 if (connect(s, (struct sockaddr *) &sa,
\r
9179 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9180 err = WSAGetLastError();
\r
9181 if (err == WSAEADDRINUSE) {
\r
9192 /* Bind stderr local socket to unused "privileged" port address
\r
9194 s2 = INVALID_SOCKET;
\r
9195 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9196 mysa.sin_family = AF_INET;
\r
9197 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9198 for (fromPort = 1023;; fromPort--) {
\r
9199 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9200 if (fromPort < 0) {
\r
9201 (void) closesocket(s);
\r
9203 return WSAEADDRINUSE;
\r
9205 if (s2 == INVALID_SOCKET) {
\r
9206 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9207 err = WSAGetLastError();
\r
9213 uport = (unsigned short) fromPort;
\r
9214 mysa.sin_port = htons(uport);
\r
9215 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9216 == SOCKET_ERROR) {
\r
9217 err = WSAGetLastError();
\r
9218 if (err == WSAEADDRINUSE) continue;
\r
9219 (void) closesocket(s);
\r
9223 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9224 err = WSAGetLastError();
\r
9225 if (err == WSAEADDRINUSE) {
\r
9227 s2 = INVALID_SOCKET;
\r
9230 (void) closesocket(s);
\r
9231 (void) closesocket(s2);
\r
9237 prevStderrPort = fromPort; // remember port used
\r
9238 sprintf(stderrPortStr, "%d", fromPort);
\r
9240 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9241 err = WSAGetLastError();
\r
9242 (void) closesocket(s);
\r
9243 (void) closesocket(s2);
\r
9248 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9249 err = WSAGetLastError();
\r
9250 (void) closesocket(s);
\r
9251 (void) closesocket(s2);
\r
9255 if (*user == NULLCHAR) user = UserName();
\r
9256 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9257 err = WSAGetLastError();
\r
9258 (void) closesocket(s);
\r
9259 (void) closesocket(s2);
\r
9263 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9264 err = WSAGetLastError();
\r
9265 (void) closesocket(s);
\r
9266 (void) closesocket(s2);
\r
9271 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9272 err = WSAGetLastError();
\r
9273 (void) closesocket(s);
\r
9274 (void) closesocket(s2);
\r
9278 (void) closesocket(s2); /* Stop listening */
\r
9280 /* Prepare return value */
\r
9281 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9282 cp->kind = CPRcmd;
\r
9285 *pr = (ProcRef *) cp;
\r
9292 AddInputSource(ProcRef pr, int lineByLine,
\r
9293 InputCallback func, VOIDSTAR closure)
\r
9295 InputSource *is, *is2 = NULL;
\r
9296 ChildProc *cp = (ChildProc *) pr;
\r
9298 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9299 is->lineByLine = lineByLine;
\r
9301 is->closure = closure;
\r
9302 is->second = NULL;
\r
9303 is->next = is->buf;
\r
9304 if (pr == NoProc) {
\r
9305 is->kind = CPReal;
\r
9306 consoleInputSource = is;
\r
9308 is->kind = cp->kind;
\r
9310 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9311 we create all threads suspended so that the is->hThread variable can be
\r
9312 safely assigned, then let the threads start with ResumeThread.
\r
9314 switch (cp->kind) {
\r
9316 is->hFile = cp->hFrom;
\r
9317 cp->hFrom = NULL; /* now owned by InputThread */
\r
9319 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9320 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9324 is->hFile = cp->hFrom;
\r
9325 cp->hFrom = NULL; /* now owned by InputThread */
\r
9327 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9328 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9332 is->sock = cp->sock;
\r
9334 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9335 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9339 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9341 is->sock = cp->sock;
\r
9343 is2->sock = cp->sock2;
\r
9344 is2->second = is2;
\r
9346 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9347 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9349 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9350 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9354 if( is->hThread != NULL ) {
\r
9355 ResumeThread( is->hThread );
\r
9358 if( is2 != NULL && is2->hThread != NULL ) {
\r
9359 ResumeThread( is2->hThread );
\r
9363 return (InputSourceRef) is;
\r
9367 RemoveInputSource(InputSourceRef isr)
\r
9371 is = (InputSource *) isr;
\r
9372 is->hThread = NULL; /* tell thread to stop */
\r
9373 CloseHandle(is->hThread);
\r
9374 if (is->second != NULL) {
\r
9375 is->second->hThread = NULL;
\r
9376 CloseHandle(is->second->hThread);
\r
9380 int no_wrap(char *message, int count)
\r
9382 ConsoleOutput(message, count, FALSE);
\r
9387 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9390 int outCount = SOCKET_ERROR;
\r
9391 ChildProc *cp = (ChildProc *) pr;
\r
9392 static OVERLAPPED ovl;
\r
9393 static int line = 0;
\r
9397 if (appData.noJoin || !appData.useInternalWrap)
\r
9398 return no_wrap(message, count);
\r
9401 int width = get_term_width();
\r
9402 int len = wrap(NULL, message, count, width, &line);
\r
9403 char *msg = malloc(len);
\r
9407 return no_wrap(message, count);
\r
9410 dbgchk = wrap(msg, message, count, width, &line);
\r
9411 if (dbgchk != len && appData.debugMode)
\r
9412 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9413 ConsoleOutput(msg, len, FALSE);
\r
9420 if (ovl.hEvent == NULL) {
\r
9421 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9423 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9425 switch (cp->kind) {
\r
9428 outCount = send(cp->sock, message, count, 0);
\r
9429 if (outCount == SOCKET_ERROR) {
\r
9430 *outError = WSAGetLastError();
\r
9432 *outError = NO_ERROR;
\r
9437 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9438 &dOutCount, NULL)) {
\r
9439 *outError = NO_ERROR;
\r
9440 outCount = (int) dOutCount;
\r
9442 *outError = GetLastError();
\r
9447 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9448 &dOutCount, &ovl);
\r
9449 if (*outError == NO_ERROR) {
\r
9450 outCount = (int) dOutCount;
\r
9458 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9461 /* Ignore delay, not implemented for WinBoard */
\r
9462 return OutputToProcess(pr, message, count, outError);
\r
9467 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9468 char *buf, int count, int error)
\r
9470 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9473 /* see wgamelist.c for Game List functions */
\r
9474 /* see wedittags.c for Edit Tags functions */
\r
9481 char buf[MSG_SIZ];
\r
9484 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9485 f = fopen(buf, "r");
\r
9487 ProcessICSInitScript(f);
\r
9495 StartAnalysisClock()
\r
9497 if (analysisTimerEvent) return;
\r
9498 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9499 (UINT) 2000, NULL);
\r
9503 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9505 highlightInfo.sq[0].x = fromX;
\r
9506 highlightInfo.sq[0].y = fromY;
\r
9507 highlightInfo.sq[1].x = toX;
\r
9508 highlightInfo.sq[1].y = toY;
\r
9514 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9515 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9519 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9521 premoveHighlightInfo.sq[0].x = fromX;
\r
9522 premoveHighlightInfo.sq[0].y = fromY;
\r
9523 premoveHighlightInfo.sq[1].x = toX;
\r
9524 premoveHighlightInfo.sq[1].y = toY;
\r
9528 ClearPremoveHighlights()
\r
9530 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9531 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9535 ShutDownFrontEnd()
\r
9537 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9538 DeleteClipboardTempFiles();
\r
9544 if (IsIconic(hwndMain))
\r
9545 ShowWindow(hwndMain, SW_RESTORE);
\r
9547 SetActiveWindow(hwndMain);
\r
9551 * Prototypes for animation support routines
\r
9553 static void ScreenSquare(int column, int row, POINT * pt);
\r
9554 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9555 POINT frames[], int * nFrames);
\r
9559 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)
\r
9560 { // [HGM] atomic: animate blast wave
\r
9562 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);
\r
9563 explodeInfo.fromX = fromX;
\r
9564 explodeInfo.fromY = fromY;
\r
9565 explodeInfo.toX = toX;
\r
9566 explodeInfo.toY = toY;
\r
9567 for(i=1; i<nFrames; i++) {
\r
9568 explodeInfo.radius = (i*180)/(nFrames-1);
\r
9569 DrawPosition(FALSE, NULL);
\r
9570 Sleep(appData.animSpeed);
\r
9572 explodeInfo.radius = 0;
\r
9573 DrawPosition(TRUE, NULL);
\r
9579 AnimateMove(board, fromX, fromY, toX, toY)
\r
9586 ChessSquare piece;
\r
9587 POINT start, finish, mid;
\r
9588 POINT frames[kFactor * 2 + 1];
\r
9591 if (!appData.animate) return;
\r
9592 if (doingSizing) return;
\r
9593 if (fromY < 0 || fromX < 0) return;
\r
9594 piece = board[fromY][fromX];
\r
9595 if (piece >= EmptySquare) return;
\r
9597 ScreenSquare(fromX, fromY, &start);
\r
9598 ScreenSquare(toX, toY, &finish);
\r
9600 /* All pieces except knights move in straight line */
\r
9601 if (piece != WhiteKnight && piece != BlackKnight) {
\r
9602 mid.x = start.x + (finish.x - start.x) / 2;
\r
9603 mid.y = start.y + (finish.y - start.y) / 2;
\r
9605 /* Knight: make diagonal movement then straight */
\r
9606 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9607 mid.x = start.x + (finish.x - start.x) / 2;
\r
9611 mid.y = start.y + (finish.y - start.y) / 2;
\r
9615 /* Don't use as many frames for very short moves */
\r
9616 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9617 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9619 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9621 animInfo.from.x = fromX;
\r
9622 animInfo.from.y = fromY;
\r
9623 animInfo.to.x = toX;
\r
9624 animInfo.to.y = toY;
\r
9625 animInfo.lastpos = start;
\r
9626 animInfo.piece = piece;
\r
9627 for (n = 0; n < nFrames; n++) {
\r
9628 animInfo.pos = frames[n];
\r
9629 DrawPosition(FALSE, NULL);
\r
9630 animInfo.lastpos = animInfo.pos;
\r
9631 Sleep(appData.animSpeed);
\r
9633 animInfo.pos = finish;
\r
9634 DrawPosition(FALSE, NULL);
\r
9635 animInfo.piece = EmptySquare;
\r
9636 if(gameInfo.variant == VariantAtomic &&
\r
9637 (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )
\r
9638 AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);
\r
9641 /* Convert board position to corner of screen rect and color */
\r
9644 ScreenSquare(column, row, pt)
\r
9645 int column; int row; POINT * pt;
\r
9648 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9649 pt->y = lineGap + row * (squareSize + lineGap);
\r
9651 pt->x = lineGap + column * (squareSize + lineGap);
\r
9652 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9656 /* Generate a series of frame coords from start->mid->finish.
\r
9657 The movement rate doubles until the half way point is
\r
9658 reached, then halves back down to the final destination,
\r
9659 which gives a nice slow in/out effect. The algorithmn
\r
9660 may seem to generate too many intermediates for short
\r
9661 moves, but remember that the purpose is to attract the
\r
9662 viewers attention to the piece about to be moved and
\r
9663 then to where it ends up. Too few frames would be less
\r
9667 Tween(start, mid, finish, factor, frames, nFrames)
\r
9668 POINT * start; POINT * mid;
\r
9669 POINT * finish; int factor;
\r
9670 POINT frames[]; int * nFrames;
\r
9672 int n, fraction = 1, count = 0;
\r
9674 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9675 for (n = 0; n < factor; n++)
\r
9677 for (n = 0; n < factor; n++) {
\r
9678 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9679 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9681 fraction = fraction / 2;
\r
9685 frames[count] = *mid;
\r
9688 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9690 for (n = 0; n < factor; n++) {
\r
9691 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9692 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9694 fraction = fraction * 2;
\r
9700 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
\r
9702 MoveHistorySet( movelist, first, last, current, pvInfoList );
\r
9704 EvalGraphSet( first, last, current, pvInfoList );
\r