2 * WinBoard.c -- Windows NT front end to XBoard
\r
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
\r
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
\r
8 * 2007, 2008, 2009, 2010, 2011, 2012, 2013 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
84 #include "frontend.h"
\r
85 #include "backend.h"
\r
86 #include "winboard.h"
\r
88 #include "wclipbrd.h"
\r
89 #include "woptions.h"
\r
90 #include "wsockerr.h"
\r
91 #include "defaults.h"
\r
96 #define DATADIR "~~"
\r
98 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
\r
100 int myrandom(void);
\r
101 void mysrandom(unsigned int seed);
\r
103 extern int whiteFlag, blackFlag;
\r
104 Boolean flipClock = FALSE;
\r
105 extern HANDLE chatHandle[];
\r
106 extern enum ICS_TYPE ics_type;
\r
108 int MySearchPath P((char *installDir, char *name, char *fullname));
\r
109 int MyGetFullPathName P((char *name, char *fullname));
\r
110 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
111 VOID NewVariantPopup(HWND hwnd);
\r
112 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
113 /*char*/int promoChar));
\r
114 void DisplayMove P((int moveNumber));
\r
115 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
\r
116 void ChatPopUp P((char *s));
\r
118 ChessSquare piece;
\r
119 POINT pos; /* window coordinates of current pos */
\r
120 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
121 POINT from; /* board coordinates of the piece's orig pos */
\r
122 POINT to; /* board coordinates of the piece's new pos */
\r
125 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
128 POINT start; /* window coordinates of start pos */
\r
129 POINT pos; /* window coordinates of current pos */
\r
130 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
131 POINT from; /* board coordinates of the piece's orig pos */
\r
135 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };
\r
138 POINT sq[2]; /* board coordinates of from, to squares */
\r
141 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
142 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
143 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
144 static HighlightInfo oldPartnerHighlight = { {{-1, -1}, {-1, -1}} };
\r
146 typedef struct { // [HGM] atomic
\r
147 int fromX, fromY, toX, toY, radius;
\r
150 static ExplodeInfo explodeInfo;
\r
152 /* Window class names */
\r
153 char szAppName[] = "WinBoard";
\r
154 char szConsoleName[] = "WBConsole";
\r
156 /* Title bar text */
\r
157 char szTitle[] = "WinBoard";
\r
158 char szConsoleTitle[] = "I C S Interaction";
\r
161 char *settingsFileName;
\r
162 Boolean saveSettingsOnExit;
\r
163 char installDir[MSG_SIZ];
\r
164 int errorExitStatus;
\r
166 BoardSize boardSize;
\r
167 Boolean chessProgram;
\r
168 //static int boardX, boardY;
\r
169 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
170 int squareSize, lineGap, minorSize, border;
\r
171 static int winW, winH;
\r
172 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
173 static int logoHeight = 0;
\r
174 static char messageText[MESSAGE_TEXT_MAX];
\r
175 static int clockTimerEvent = 0;
\r
176 static int loadGameTimerEvent = 0;
\r
177 static int analysisTimerEvent = 0;
\r
178 static DelayedEventCallback delayedTimerCallback;
\r
179 static int delayedTimerEvent = 0;
\r
180 static int buttonCount = 2;
\r
181 char *icsTextMenuString;
\r
183 char *firstChessProgramNames;
\r
184 char *secondChessProgramNames;
\r
186 #define PALETTESIZE 256
\r
188 HINSTANCE hInst; /* current instance */
\r
189 Boolean alwaysOnTop = FALSE;
\r
191 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
192 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
194 ColorClass currentColorClass;
\r
196 static HWND savedHwnd;
\r
197 HWND hCommPort = NULL; /* currently open comm port */
\r
198 static HWND hwndPause; /* pause button */
\r
199 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
200 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
201 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
202 explodeBrush, /* [HGM] atomic */
\r
203 markerBrush, /* [HGM] markers */
\r
204 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
205 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
206 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
207 static HPEN gridPen = NULL;
\r
208 static HPEN highlightPen = NULL;
\r
209 static HPEN premovePen = NULL;
\r
210 static NPLOGPALETTE pLogPal;
\r
211 static BOOL paletteChanged = FALSE;
\r
212 static HICON iconWhite, iconBlack, iconCurrent;
\r
213 static int doingSizing = FALSE;
\r
214 static int lastSizing = 0;
\r
215 static int prevStderrPort;
\r
216 static HBITMAP userLogo;
\r
218 static HBITMAP liteBackTexture = NULL;
\r
219 static HBITMAP darkBackTexture = NULL;
\r
220 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
221 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
222 static int backTextureSquareSize = 0;
\r
223 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
225 #if __GNUC__ && !defined(_winmajor)
\r
226 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
231 #if defined(_winmajor)
\r
232 #define oldDialog (_winmajor < 4)
\r
234 #define oldDialog 0
\r
238 #define INTERNATIONAL
\r
240 #ifdef INTERNATIONAL
\r
241 # define _(s) T_(s)
\r
247 # define Translate(x, y)
\r
248 # define LoadLanguageFile(s)
\r
251 #ifdef INTERNATIONAL
\r
253 Boolean barbaric; // flag indicating if translation is needed
\r
255 // list of item numbers used in each dialog (used to alter language at run time)
\r
257 #define ABOUTBOX -1 /* not sure why these are needed */
\r
258 #define ABOUTBOX2 -1
\r
260 int dialogItems[][42] = {
\r
261 { ABOUTBOX, IDOK, OPT_MESS, 400 },
\r
262 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed,
\r
263 OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors, IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL },
\r
264 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,
\r
265 OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, IDOK, IDCANCEL },
\r
266 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
267 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
268 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
269 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
270 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
271 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
272 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
273 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
274 { ABOUTBOX2, IDC_ChessBoard },
\r
275 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
276 OPT_GameListClose, IDC_GameListDoFilter },
\r
277 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
278 { DLG_Error, IDOK },
\r
279 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
280 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
281 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
282 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
283 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
284 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
285 { DLG_IndexNumber, IDC_Index },
\r
286 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
287 { DLG_TypeInName, IDOK, IDCANCEL },
\r
288 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
289 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
290 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
291 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
292 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
293 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
294 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
295 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
296 OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove },
\r
297 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
298 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
299 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
300 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
301 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
302 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
303 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
304 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
305 GPB_General, GPB_Alarm, OPT_AutoCreate },
\r
306 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
307 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
308 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
309 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
310 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
311 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
312 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
313 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid },
\r
314 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
315 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
316 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
317 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
318 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
319 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
320 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,
\r
321 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
322 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
323 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
324 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
325 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,
\r
326 OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7,
\r
327 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
328 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
329 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
330 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
331 { DLG_MoveHistory },
\r
332 { DLG_EvalGraph },
\r
333 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
334 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
335 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
336 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
337 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
338 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
339 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
340 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
341 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
345 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];
\r
346 static int lastChecked;
\r
347 static char oldLanguage[MSG_SIZ], *menuText[10][30];
\r
348 extern int tinyLayout;
\r
349 extern char * menuBarText[][10];
\r
352 LoadLanguageFile(char *name)
\r
353 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
355 int i=0, j=0, n=0, k;
\r
358 if(!name || name[0] == NULLCHAR) return;
\r
359 snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension
\r
360 appData.language = oldLanguage;
\r
361 if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on
\r
362 if((f = fopen(buf, "r")) == NULL) return;
\r
363 while((k = fgetc(f)) != EOF) {
\r
364 if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }
\r
365 languageBuf[i] = k;
\r
367 if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {
\r
369 if(p = strstr(languageBuf + n + 1, "\" === \"")) {
\r
370 if(p > languageBuf+n+2 && p+8 < languageBuf+i) {
\r
371 if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }
\r
372 english[j] = languageBuf + n + 1; *p = 0;
\r
373 foreign[j++] = p + 7; languageBuf[i-1] = 0;
\r
374 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);
\r
379 } else if(i > 0 && languageBuf[i-1] == '\\') {
\r
381 case 'n': k = '\n'; break;
\r
382 case 'r': k = '\r'; break;
\r
383 case 't': k = '\t'; break;
\r
385 languageBuf[--i] = k;
\r
390 barbaric = (j != 0);
\r
391 safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );
\r
396 { // return the translation of the given string
\r
397 // efficiency can be improved a lot...
\r
399 static char buf[MSG_SIZ];
\r
400 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
401 if(!barbaric) return s;
\r
402 if(!s) return ""; // sanity
\r
403 while(english[i]) {
\r
404 if(!strcmp(s, english[i])) return foreign[i];
\r
405 if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending
\r
406 snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion
\r
415 Translate(HWND hDlg, int dialogID)
\r
416 { // translate all text items in the given dialog
\r
418 char buf[MSG_SIZ], *s;
\r
419 if(!barbaric) return;
\r
420 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
421 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
422 GetWindowText( hDlg, buf, MSG_SIZ );
\r
424 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
425 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
426 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
427 if(strlen(buf) == 0) continue;
\r
429 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
434 TranslateOneMenu(int i, HMENU subMenu)
\r
437 static MENUITEMINFO info;
\r
439 info.cbSize = sizeof(MENUITEMINFO);
\r
440 info.fMask = MIIM_STATE | MIIM_TYPE;
\r
441 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
443 info.dwTypeData = buf;
\r
444 info.cch = sizeof(buf);
\r
445 GetMenuItemInfo(subMenu, j, TRUE, &info);
\r
447 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );
\r
448 else menuText[i][j] = strdup(buf); // remember original on first change
\r
450 if(buf[0] == NULLCHAR) continue;
\r
451 info.dwTypeData = T_(buf);
\r
452 info.cch = strlen(buf)+1;
\r
453 SetMenuItemInfo(subMenu, j, TRUE, &info);
\r
459 TranslateMenus(int addLanguage)
\r
462 WIN32_FIND_DATA fileData;
\r
464 #define IDM_English 1970
\r
466 HMENU mainMenu = GetMenu(hwndMain);
\r
467 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
468 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
469 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
470 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
471 TranslateOneMenu(i, subMenu);
\r
473 DrawMenuBar(hwndMain);
\r
476 if(!addLanguage) return;
\r
477 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
478 HMENU mainMenu = GetMenu(hwndMain);
\r
479 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
480 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
481 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
482 i = 0; lastChecked = IDM_English;
\r
484 char *p, *q = fileData.cFileName;
\r
485 int checkFlag = MF_UNCHECKED;
\r
486 languageFile[i] = strdup(q);
\r
487 if(barbaric && !strcmp(oldLanguage, q)) {
\r
488 checkFlag = MF_CHECKED;
\r
489 lastChecked = IDM_English + i + 1;
\r
490 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
492 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
493 p = strstr(fileData.cFileName, ".lng");
\r
495 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
496 } while(FindNextFile(hFind, &fileData));
\r
503 #define IDM_RecentEngines 3000
\r
506 RecentEngineMenu (char *s)
\r
508 if(appData.icsActive) return;
\r
509 if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty
\r
510 HMENU mainMenu = GetMenu(hwndMain);
\r
511 HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu
\r
512 int i=IDM_RecentEngines;
\r
513 recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu
\r
514 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
516 char *p = strchr(s, '\n');
\r
517 if(p == NULL) return; // malformed!
\r
519 AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);
\r
533 int cliWidth, cliHeight;
\r
536 SizeInfo sizeInfo[] =
\r
538 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
539 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
540 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
541 { "petite", 33, 1, 1, 1, 0, 0 },
\r
542 { "slim", 37, 2, 1, 0, 0, 0 },
\r
543 { "small", 40, 2, 1, 0, 0, 0 },
\r
544 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
545 { "middling", 49, 2, 0, 0, 0, 0 },
\r
546 { "average", 54, 2, 0, 0, 0, 0 },
\r
547 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
548 { "medium", 64, 3, 0, 0, 0, 0 },
\r
549 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
550 { "large", 80, 3, 0, 0, 0, 0 },
\r
551 { "big", 87, 3, 0, 0, 0, 0 },
\r
552 { "huge", 95, 3, 0, 0, 0, 0 },
\r
553 { "giant", 108, 3, 0, 0, 0, 0 },
\r
554 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
555 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
556 { NULL, 0, 0, 0, 0, 0, 0 }
\r
559 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
560 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
562 { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
563 { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
564 { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
565 { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
566 { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
567 { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
568 { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
569 { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
570 { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
571 { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
572 { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
573 { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
574 { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
575 { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
576 { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
577 { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
578 { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL), MF (GAMELIST_FONT_ALL) },
\r
579 { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
582 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
591 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
592 #define N_BUTTONS 5
\r
594 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
596 {"<<", IDM_ToStart, NULL, NULL},
\r
597 {"<", IDM_Backward, NULL, NULL},
\r
598 {"P", IDM_Pause, NULL, NULL},
\r
599 {">", IDM_Forward, NULL, NULL},
\r
600 {">>", IDM_ToEnd, NULL, NULL},
\r
603 int tinyLayout = 0, smallLayout = 0;
\r
604 #define MENU_BAR_ITEMS 9
\r
605 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
606 { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },
\r
607 { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },
\r
611 MySound sounds[(int)NSoundClasses];
\r
612 MyTextAttribs textAttribs[(int)NColorClasses];
\r
614 MyColorizeAttribs colorizeAttribs[] = {
\r
615 { (COLORREF)0, 0, N_("Shout Text") },
\r
616 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
617 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
618 { (COLORREF)0, 0, N_("Channel Text") },
\r
619 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
620 { (COLORREF)0, 0, N_("Tell Text") },
\r
621 { (COLORREF)0, 0, N_("Challenge Text") },
\r
622 { (COLORREF)0, 0, N_("Request Text") },
\r
623 { (COLORREF)0, 0, N_("Seek Text") },
\r
624 { (COLORREF)0, 0, N_("Normal Text") },
\r
625 { (COLORREF)0, 0, N_("None") }
\r
630 static char *commentTitle;
\r
631 static char *commentText;
\r
632 static int commentIndex;
\r
633 static Boolean editComment = FALSE;
\r
636 char errorTitle[MSG_SIZ];
\r
637 char errorMessage[2*MSG_SIZ];
\r
638 HWND errorDialog = NULL;
\r
639 BOOLEAN moveErrorMessageUp = FALSE;
\r
640 BOOLEAN consoleEcho = TRUE;
\r
641 CHARFORMAT consoleCF;
\r
642 COLORREF consoleBackgroundColor;
\r
644 char *programVersion;
\r
650 typedef int CPKind;
\r
659 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
662 #define INPUT_SOURCE_BUF_SIZE 4096
\r
664 typedef struct _InputSource {
\r
671 char buf[INPUT_SOURCE_BUF_SIZE];
\r
675 InputCallback func;
\r
676 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
680 InputSource *consoleInputSource;
\r
685 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
686 VOID ConsoleCreate();
\r
688 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
689 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
690 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
691 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
693 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
694 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
695 void ParseIcsTextMenu(char *icsTextMenuString);
\r
696 VOID PopUpNameDialog(char firstchar);
\r
697 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
701 int GameListOptions();
\r
703 int dummy; // [HGM] for obsolete args
\r
705 HWND hwndMain = NULL; /* root window*/
\r
706 HWND hwndConsole = NULL;
\r
707 HWND commentDialog = NULL;
\r
708 HWND moveHistoryDialog = NULL;
\r
709 HWND evalGraphDialog = NULL;
\r
710 HWND engineOutputDialog = NULL;
\r
711 HWND gameListDialog = NULL;
\r
712 HWND editTagsDialog = NULL;
\r
714 int commentUp = FALSE;
\r
716 WindowPlacement wpMain;
\r
717 WindowPlacement wpConsole;
\r
718 WindowPlacement wpComment;
\r
719 WindowPlacement wpMoveHistory;
\r
720 WindowPlacement wpEvalGraph;
\r
721 WindowPlacement wpEngineOutput;
\r
722 WindowPlacement wpGameList;
\r
723 WindowPlacement wpTags;
\r
725 VOID EngineOptionsPopup(); // [HGM] settings
\r
727 VOID GothicPopUp(char *title, VariantClass variant);
\r
729 * Setting "frozen" should disable all user input other than deleting
\r
730 * the window. We do this while engines are initializing themselves.
\r
732 static int frozen = 0;
\r
733 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
739 if (frozen) return;
\r
741 hmenu = GetMenu(hwndMain);
\r
742 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
743 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
745 DrawMenuBar(hwndMain);
\r
748 /* Undo a FreezeUI */
\r
754 if (!frozen) return;
\r
756 hmenu = GetMenu(hwndMain);
\r
757 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
758 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
760 DrawMenuBar(hwndMain);
\r
763 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
765 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
771 #define JAWS_ALT_INTERCEPT
\r
772 #define JAWS_KBUP_NAVIGATION
\r
773 #define JAWS_KBDOWN_NAVIGATION
\r
774 #define JAWS_MENU_ITEMS
\r
775 #define JAWS_SILENCE
\r
776 #define JAWS_REPLAY
\r
778 #define JAWS_COPYRIGHT
\r
779 #define JAWS_DELETE(X) X
\r
780 #define SAYMACHINEMOVE()
\r
784 /*---------------------------------------------------------------------------*\
\r
788 \*---------------------------------------------------------------------------*/
\r
791 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
792 LPSTR lpCmdLine, int nCmdShow)
\r
795 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
796 // INITCOMMONCONTROLSEX ex;
\r
800 LoadLibrary("RICHED32.DLL");
\r
801 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
803 if (!InitApplication(hInstance)) {
\r
806 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
813 // InitCommonControlsEx(&ex);
\r
814 InitCommonControls();
\r
816 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
817 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
818 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
820 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
822 while (GetMessage(&msg, /* message structure */
\r
823 NULL, /* handle of window receiving the message */
\r
824 0, /* lowest message to examine */
\r
825 0)) /* highest message to examine */
\r
828 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
829 // [HGM] navigate: switch between all windows with tab
\r
830 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
831 int i, currentElement = 0;
\r
833 // first determine what element of the chain we come from (if any)
\r
834 if(appData.icsActive) {
\r
835 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
836 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
838 if(engineOutputDialog && EngineOutputIsUp()) {
\r
839 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
840 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
842 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
843 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
845 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
846 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
847 if(msg.hwnd == e1) currentElement = 2; else
\r
848 if(msg.hwnd == e2) currentElement = 3; else
\r
849 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
850 if(msg.hwnd == mh) currentElement = 4; else
\r
851 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
852 if(msg.hwnd == hText) currentElement = 5; else
\r
853 if(msg.hwnd == hInput) currentElement = 6; else
\r
854 for (i = 0; i < N_BUTTONS; i++) {
\r
855 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
858 // determine where to go to
\r
859 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
861 currentElement = (currentElement + direction) % 7;
\r
862 switch(currentElement) {
\r
864 h = hwndMain; break; // passing this case always makes the loop exit
\r
866 h = buttonDesc[0].hwnd; break; // could be NULL
\r
868 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
871 if(!EngineOutputIsUp()) continue;
\r
874 if(!MoveHistoryIsUp()) continue;
\r
876 // case 6: // input to eval graph does not seem to get here!
\r
877 // if(!EvalGraphIsUp()) continue;
\r
878 // h = evalGraphDialog; break;
\r
880 if(!appData.icsActive) continue;
\r
884 if(!appData.icsActive) continue;
\r
890 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
891 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
894 continue; // this message now has been processed
\r
898 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
899 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
900 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
901 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
902 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
903 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
904 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
905 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
906 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
907 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
908 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
909 for(i=0; i<MAX_CHAT; i++)
\r
910 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
913 if(done) continue; // [HGM] chat: end patch
\r
914 TranslateMessage(&msg); /* Translates virtual key codes */
\r
915 DispatchMessage(&msg); /* Dispatches message to window */
\r
920 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
923 /*---------------------------------------------------------------------------*\
\r
925 * Initialization functions
\r
927 \*---------------------------------------------------------------------------*/
\r
931 { // update user logo if necessary
\r
932 static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;
\r
934 if(appData.autoLogo) {
\r
935 curName = UserName();
\r
936 if(strcmp(curName, oldUserName)) {
\r
937 GetCurrentDirectory(MSG_SIZ, dir);
\r
938 SetCurrentDirectory(installDir);
\r
939 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
940 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
941 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
942 if(userLogo == NULL)
\r
943 userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
944 SetCurrentDirectory(dir); /* return to prev directory */
\r
950 InitApplication(HINSTANCE hInstance)
\r
954 /* Fill in window class structure with parameters that describe the */
\r
957 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
958 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
959 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
960 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
961 wc.hInstance = hInstance; /* Owner of this class */
\r
962 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
963 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
964 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
965 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
966 wc.lpszClassName = szAppName; /* Name to register as */
\r
968 /* Register the window class and return success/failure code. */
\r
969 if (!RegisterClass(&wc)) return FALSE;
\r
971 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
972 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
974 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
975 wc.hInstance = hInstance;
\r
976 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
977 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
978 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
979 wc.lpszMenuName = NULL;
\r
980 wc.lpszClassName = szConsoleName;
\r
982 if (!RegisterClass(&wc)) return FALSE;
\r
987 /* Set by InitInstance, used by EnsureOnScreen */
\r
988 int screenHeight, screenWidth;
\r
989 RECT screenGeometry;
\r
992 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
994 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
995 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
996 if (*x > screenGeometry.right - 32) *x = screenGeometry.left;
\r
997 if (*y > screenGeometry.bottom - 32) *y = screenGeometry.top;
\r
998 if (*x < screenGeometry.left + minX) *x = screenGeometry.left + minX;
\r
999 if (*y < screenGeometry.top + minY) *y = screenGeometry.top + minY;
\r
1003 LoadLogo(ChessProgramState *cps, int n, Boolean ics)
\r
1005 char buf[MSG_SIZ], dir[MSG_SIZ];
\r
1006 GetCurrentDirectory(MSG_SIZ, dir);
\r
1007 SetCurrentDirectory(installDir);
\r
1008 if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {
\r
1009 cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1011 if (cps->programLogo == NULL && appData.debugMode) {
\r
1012 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );
\r
1014 } else if(appData.autoLogo) {
\r
1015 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
\r
1016 char *opponent = "";
\r
1017 if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;
\r
1018 if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;
\r
1019 sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);
\r
1020 if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {
\r
1021 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
1022 cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1025 if(appData.directory[n] && appData.directory[n][0]) {
\r
1026 SetCurrentDirectory(appData.directory[n]);
\r
1027 cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1030 SetCurrentDirectory(dir); /* return to prev directory */
\r
1036 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
1037 backTextureSquareSize = 0; // kludge to force recalculation of texturemode
\r
1039 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1040 if(liteBackTexture) DeleteObject(liteBackTexture);
\r
1041 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1042 liteBackTextureMode = appData.liteBackTextureMode;
\r
1044 if (liteBackTexture == NULL && appData.debugMode) {
\r
1045 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1049 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1050 if(darkBackTexture) DeleteObject(darkBackTexture);
\r
1051 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1052 darkBackTextureMode = appData.darkBackTextureMode;
\r
1054 if (darkBackTexture == NULL && appData.debugMode) {
\r
1055 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1060 #ifndef SM_CXVIRTUALSCREEN
\r
1061 #define SM_CXVIRTUALSCREEN 78
\r
1063 #ifndef SM_CYVIRTUALSCREEN
\r
1064 #define SM_CYVIRTUALSCREEN 79
\r
1066 #ifndef SM_XVIRTUALSCREEN
\r
1067 #define SM_XVIRTUALSCREEN 76
\r
1069 #ifndef SM_YVIRTUALSCREEN
\r
1070 #define SM_YVIRTUALSCREEN 77
\r
1076 screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
\r
1077 if( !screenHeight ) screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1078 screenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
\r
1079 if( !screenWidth ) screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1080 screenGeometry.left = GetSystemMetrics(SM_XVIRTUALSCREEN);
\r
1081 screenGeometry.top = GetSystemMetrics(SM_YVIRTUALSCREEN);
\r
1082 screenGeometry.right = screenGeometry.left + screenWidth;
\r
1083 screenGeometry.bottom = screenGeometry.top + screenHeight;
\r
1087 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
1089 HWND hwnd; /* Main window handle. */
\r
1091 WINDOWPLACEMENT wp;
\r
1094 hInst = hInstance; /* Store instance handle in our global variable */
\r
1095 programName = szAppName;
\r
1097 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
1098 *filepart = NULLCHAR;
\r
1099 SetCurrentDirectory(installDir);
\r
1101 GetCurrentDirectory(MSG_SIZ, installDir);
\r
1103 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
1105 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
1106 /* xboard, and older WinBoards, controlled the move sound with the
\r
1107 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
1108 always turn the option on (so that the backend will call us),
\r
1109 then let the user turn the sound off by setting it to silence if
\r
1110 desired. To accommodate old winboard.ini files saved by old
\r
1111 versions of WinBoard, we also turn off the sound if the option
\r
1112 was initially set to false. [HGM] taken out of InitAppData */
\r
1113 if (!appData.ringBellAfterMoves) {
\r
1114 sounds[(int)SoundMove].name = strdup("");
\r
1115 appData.ringBellAfterMoves = TRUE;
\r
1117 if (appData.debugMode) {
\r
1118 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
1119 setbuf(debugFP, NULL);
\r
1122 LoadLanguageFile(appData.language);
\r
1126 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
1127 // InitEngineUCI( installDir, &second );
\r
1129 /* Create a main window for this application instance. */
\r
1130 hwnd = CreateWindow(szAppName, szTitle,
\r
1131 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
1132 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
1133 NULL, NULL, hInstance, NULL);
\r
1136 /* If window could not be created, return "failure" */
\r
1141 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1142 LoadLogo(&first, 0, FALSE);
\r
1143 LoadLogo(&second, 1, appData.icsActive);
\r
1147 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1148 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1149 iconCurrent = iconWhite;
\r
1150 InitDrawingColors();
\r
1152 InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args
\r
1153 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1154 /* Compute window size for each board size, and use the largest
\r
1155 size that fits on this screen as the default. */
\r
1156 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1157 if (boardSize == (BoardSize)-1 &&
\r
1158 winH <= screenHeight
\r
1159 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1160 && winW <= screenWidth) {
\r
1161 boardSize = (BoardSize)ibs;
\r
1165 InitDrawingSizes(boardSize, 0);
\r
1166 RecentEngineMenu(appData.recentEngineList);
\r
1168 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1170 /* [AS] Load textures if specified */
\r
1173 mysrandom( (unsigned) time(NULL) );
\r
1175 /* [AS] Restore layout */
\r
1176 if( wpMoveHistory.visible ) {
\r
1177 MoveHistoryPopUp();
\r
1180 if( wpEvalGraph.visible ) {
\r
1184 if( wpEngineOutput.visible ) {
\r
1185 EngineOutputPopUp();
\r
1188 /* Make the window visible; update its client area; and return "success" */
\r
1189 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1190 wp.length = sizeof(WINDOWPLACEMENT);
\r
1192 wp.showCmd = nCmdShow;
\r
1193 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1194 wp.rcNormalPosition.left = wpMain.x;
\r
1195 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1196 wp.rcNormalPosition.top = wpMain.y;
\r
1197 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1198 SetWindowPlacement(hwndMain, &wp);
\r
1200 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1202 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1203 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1205 if (hwndConsole) {
\r
1207 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1208 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1210 ShowWindow(hwndConsole, nCmdShow);
\r
1211 SetActiveWindow(hwndConsole);
\r
1213 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1214 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1223 HMENU hmenu = GetMenu(hwndMain);
\r
1225 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1226 MF_BYCOMMAND|((appData.icsActive &&
\r
1227 *appData.icsCommPort != NULLCHAR) ?
\r
1228 MF_ENABLED : MF_GRAYED));
\r
1229 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1230 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1231 MF_CHECKED : MF_UNCHECKED));
\r
1234 //---------------------------------------------------------------------------------------------------------
\r
1236 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1237 #define XBOARD FALSE
\r
1239 #define OPTCHAR "/"
\r
1240 #define SEPCHAR "="
\r
1241 #define TOPLEVEL 0
\r
1245 // front-end part of option handling
\r
1248 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1250 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1251 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1254 lf->lfEscapement = 0;
\r
1255 lf->lfOrientation = 0;
\r
1256 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1257 lf->lfItalic = mfp->italic;
\r
1258 lf->lfUnderline = mfp->underline;
\r
1259 lf->lfStrikeOut = mfp->strikeout;
\r
1260 lf->lfCharSet = mfp->charset;
\r
1261 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1262 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1263 lf->lfQuality = DEFAULT_QUALITY;
\r
1264 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1265 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1269 CreateFontInMF(MyFont *mf)
\r
1271 LFfromMFP(&mf->lf, &mf->mfp);
\r
1272 if (mf->hf) DeleteObject(mf->hf);
\r
1273 mf->hf = CreateFontIndirect(&mf->lf);
\r
1276 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1278 colorVariable[] = {
\r
1279 &whitePieceColor,
\r
1280 &blackPieceColor,
\r
1281 &lightSquareColor,
\r
1282 &darkSquareColor,
\r
1283 &highlightSquareColor,
\r
1284 &premoveHighlightColor,
\r
1286 &consoleBackgroundColor,
\r
1287 &appData.fontForeColorWhite,
\r
1288 &appData.fontBackColorWhite,
\r
1289 &appData.fontForeColorBlack,
\r
1290 &appData.fontBackColorBlack,
\r
1291 &appData.evalHistColorWhite,
\r
1292 &appData.evalHistColorBlack,
\r
1293 &appData.highlightArrowColor,
\r
1296 /* Command line font name parser. NULL name means do nothing.
\r
1297 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1298 For backward compatibility, syntax without the colon is also
\r
1299 accepted, but font names with digits in them won't work in that case.
\r
1302 ParseFontName(char *name, MyFontParams *mfp)
\r
1305 if (name == NULL) return;
\r
1307 q = strchr(p, ':');
\r
1309 if (q - p >= sizeof(mfp->faceName))
\r
1310 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1311 memcpy(mfp->faceName, p, q - p);
\r
1312 mfp->faceName[q - p] = NULLCHAR;
\r
1315 q = mfp->faceName;
\r
1317 while (*p && !isdigit(*p)) {
\r
1319 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1320 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1322 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1325 if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);
\r
1326 mfp->pointSize = (float) atof(p);
\r
1327 mfp->bold = (strchr(p, 'b') != NULL);
\r
1328 mfp->italic = (strchr(p, 'i') != NULL);
\r
1329 mfp->underline = (strchr(p, 'u') != NULL);
\r
1330 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1331 mfp->charset = DEFAULT_CHARSET;
\r
1332 q = strchr(p, 'c');
\r
1334 mfp->charset = (BYTE) atoi(q+1);
\r
1338 ParseFont(char *name, int number)
\r
1339 { // wrapper to shield back-end from 'font'
\r
1340 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1345 { // in WB we have a 2D array of fonts; this initializes their description
\r
1347 /* Point font array elements to structures and
\r
1348 parse default font names */
\r
1349 for (i=0; i<NUM_FONTS; i++) {
\r
1350 for (j=0; j<NUM_SIZES; j++) {
\r
1351 font[j][i] = &fontRec[j][i];
\r
1352 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1359 { // here we create the actual fonts from the selected descriptions
\r
1361 for (i=0; i<NUM_FONTS; i++) {
\r
1362 for (j=0; j<NUM_SIZES; j++) {
\r
1363 CreateFontInMF(font[j][i]);
\r
1367 /* Color name parser.
\r
1368 X version accepts X color names, but this one
\r
1369 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1371 ParseColorName(char *name)
\r
1373 int red, green, blue, count;
\r
1374 char buf[MSG_SIZ];
\r
1376 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1378 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1379 &red, &green, &blue);
\r
1382 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1383 DisplayError(buf, 0);
\r
1384 return RGB(0, 0, 0);
\r
1386 return PALETTERGB(red, green, blue);
\r
1390 ParseColor(int n, char *name)
\r
1391 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1392 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1396 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1398 char *e = argValue;
\r
1402 if (*e == 'b') eff |= CFE_BOLD;
\r
1403 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1404 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1405 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1406 else if (*e == '#' || isdigit(*e)) break;
\r
1410 *color = ParseColorName(e);
\r
1414 ParseTextAttribs(ColorClass cc, char *s)
\r
1415 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1416 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1417 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1421 ParseBoardSize(void *addr, char *name)
\r
1422 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1423 BoardSize bs = SizeTiny;
\r
1424 while (sizeInfo[bs].name != NULL) {
\r
1425 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1426 *(BoardSize *)addr = bs;
\r
1431 ExitArgError(_("Unrecognized board size value"), name, TRUE);
\r
1436 { // [HGM] import name from appData first
\r
1439 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1440 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1441 textAttribs[cc].sound.data = NULL;
\r
1442 MyLoadSound(&textAttribs[cc].sound);
\r
1444 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1445 textAttribs[cc].sound.name = strdup("");
\r
1446 textAttribs[cc].sound.data = NULL;
\r
1448 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1449 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1450 sounds[sc].data = NULL;
\r
1451 MyLoadSound(&sounds[sc]);
\r
1456 SetCommPortDefaults()
\r
1458 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1459 dcb.DCBlength = sizeof(DCB);
\r
1460 dcb.BaudRate = 9600;
\r
1461 dcb.fBinary = TRUE;
\r
1462 dcb.fParity = FALSE;
\r
1463 dcb.fOutxCtsFlow = FALSE;
\r
1464 dcb.fOutxDsrFlow = FALSE;
\r
1465 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1466 dcb.fDsrSensitivity = FALSE;
\r
1467 dcb.fTXContinueOnXoff = TRUE;
\r
1468 dcb.fOutX = FALSE;
\r
1470 dcb.fNull = FALSE;
\r
1471 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1472 dcb.fAbortOnError = FALSE;
\r
1474 dcb.Parity = SPACEPARITY;
\r
1475 dcb.StopBits = ONESTOPBIT;
\r
1478 // [HGM] args: these three cases taken out to stay in front-end
\r
1480 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1481 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1482 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1483 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1485 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1486 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1487 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1488 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1489 ad->argName, mfp->faceName, mfp->pointSize,
\r
1490 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1491 mfp->bold ? "b" : "",
\r
1492 mfp->italic ? "i" : "",
\r
1493 mfp->underline ? "u" : "",
\r
1494 mfp->strikeout ? "s" : "",
\r
1495 (int)mfp->charset);
\r
1501 { // [HGM] copy the names from the internal WB variables to appData
\r
1504 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1505 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1506 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1507 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1511 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1512 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1513 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1514 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1515 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1516 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1517 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1518 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1519 (ta->effects) ? " " : "",
\r
1520 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1524 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1525 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1526 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1527 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1528 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1532 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1533 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1534 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1538 ParseCommPortSettings(char *s)
\r
1539 { // wrapper to keep dcb from back-end
\r
1540 ParseCommSettings(s, &dcb);
\r
1545 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1546 GetActualPlacement(hwndMain, &wpMain);
\r
1547 GetActualPlacement(hwndConsole, &wpConsole);
\r
1548 GetActualPlacement(commentDialog, &wpComment);
\r
1549 GetActualPlacement(editTagsDialog, &wpTags);
\r
1550 GetActualPlacement(gameListDialog, &wpGameList);
\r
1551 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1552 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1553 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1557 PrintCommPortSettings(FILE *f, char *name)
\r
1558 { // wrapper to shield back-end from DCB
\r
1559 PrintCommSettings(f, name, &dcb);
\r
1563 MySearchPath(char *installDir, char *name, char *fullname)
\r
1565 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1566 if(name[0]== '%') {
\r
1567 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1568 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1569 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1570 *strchr(buf, '%') = 0;
\r
1571 strcat(fullname, getenv(buf));
\r
1572 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1574 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1575 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1576 return (int) strlen(fullname);
\r
1578 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1582 MyGetFullPathName(char *name, char *fullname)
\r
1585 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1590 { // [HGM] args: allows testing if main window is realized from back-end
\r
1591 return hwndMain != NULL;
\r
1595 PopUpStartupDialog()
\r
1599 LoadLanguageFile(appData.language);
\r
1600 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1601 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1602 FreeProcInstance(lpProc);
\r
1605 /*---------------------------------------------------------------------------*\
\r
1607 * GDI board drawing routines
\r
1609 \*---------------------------------------------------------------------------*/
\r
1611 /* [AS] Draw square using background texture */
\r
1612 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1617 return; /* Should never happen! */
\r
1620 SetGraphicsMode( dst, GM_ADVANCED );
\r
1627 /* X reflection */
\r
1632 x.eDx = (FLOAT) dw + dx - 1;
\r
1635 SetWorldTransform( dst, &x );
\r
1638 /* Y reflection */
\r
1644 x.eDy = (FLOAT) dh + dy - 1;
\r
1646 SetWorldTransform( dst, &x );
\r
1654 x.eDx = (FLOAT) dx;
\r
1655 x.eDy = (FLOAT) dy;
\r
1658 SetWorldTransform( dst, &x );
\r
1662 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1670 SetWorldTransform( dst, &x );
\r
1672 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1675 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1677 PM_WP = (int) WhitePawn,
\r
1678 PM_WN = (int) WhiteKnight,
\r
1679 PM_WB = (int) WhiteBishop,
\r
1680 PM_WR = (int) WhiteRook,
\r
1681 PM_WQ = (int) WhiteQueen,
\r
1682 PM_WF = (int) WhiteFerz,
\r
1683 PM_WW = (int) WhiteWazir,
\r
1684 PM_WE = (int) WhiteAlfil,
\r
1685 PM_WM = (int) WhiteMan,
\r
1686 PM_WO = (int) WhiteCannon,
\r
1687 PM_WU = (int) WhiteUnicorn,
\r
1688 PM_WH = (int) WhiteNightrider,
\r
1689 PM_WA = (int) WhiteAngel,
\r
1690 PM_WC = (int) WhiteMarshall,
\r
1691 PM_WAB = (int) WhiteCardinal,
\r
1692 PM_WD = (int) WhiteDragon,
\r
1693 PM_WL = (int) WhiteLance,
\r
1694 PM_WS = (int) WhiteCobra,
\r
1695 PM_WV = (int) WhiteFalcon,
\r
1696 PM_WSG = (int) WhiteSilver,
\r
1697 PM_WG = (int) WhiteGrasshopper,
\r
1698 PM_WK = (int) WhiteKing,
\r
1699 PM_BP = (int) BlackPawn,
\r
1700 PM_BN = (int) BlackKnight,
\r
1701 PM_BB = (int) BlackBishop,
\r
1702 PM_BR = (int) BlackRook,
\r
1703 PM_BQ = (int) BlackQueen,
\r
1704 PM_BF = (int) BlackFerz,
\r
1705 PM_BW = (int) BlackWazir,
\r
1706 PM_BE = (int) BlackAlfil,
\r
1707 PM_BM = (int) BlackMan,
\r
1708 PM_BO = (int) BlackCannon,
\r
1709 PM_BU = (int) BlackUnicorn,
\r
1710 PM_BH = (int) BlackNightrider,
\r
1711 PM_BA = (int) BlackAngel,
\r
1712 PM_BC = (int) BlackMarshall,
\r
1713 PM_BG = (int) BlackGrasshopper,
\r
1714 PM_BAB = (int) BlackCardinal,
\r
1715 PM_BD = (int) BlackDragon,
\r
1716 PM_BL = (int) BlackLance,
\r
1717 PM_BS = (int) BlackCobra,
\r
1718 PM_BV = (int) BlackFalcon,
\r
1719 PM_BSG = (int) BlackSilver,
\r
1720 PM_BK = (int) BlackKing
\r
1723 static HFONT hPieceFont = NULL;
\r
1724 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1725 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1726 static int fontBitmapSquareSize = 0;
\r
1727 static char pieceToFontChar[(int) EmptySquare] =
\r
1728 { 'p', 'n', 'b', 'r', 'q',
\r
1729 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1730 'k', 'o', 'm', 'v', 't', 'w',
\r
1731 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1734 extern BOOL SetCharTable( char *table, const char * map );
\r
1735 /* [HGM] moved to backend.c */
\r
1737 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1740 BYTE r1 = GetRValue( color );
\r
1741 BYTE g1 = GetGValue( color );
\r
1742 BYTE b1 = GetBValue( color );
\r
1748 /* Create a uniform background first */
\r
1749 hbrush = CreateSolidBrush( color );
\r
1750 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1751 FillRect( hdc, &rc, hbrush );
\r
1752 DeleteObject( hbrush );
\r
1755 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1756 int steps = squareSize / 2;
\r
1759 for( i=0; i<steps; i++ ) {
\r
1760 BYTE r = r1 - (r1-r2) * i / steps;
\r
1761 BYTE g = g1 - (g1-g2) * i / steps;
\r
1762 BYTE b = b1 - (b1-b2) * i / steps;
\r
1764 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1765 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1766 FillRect( hdc, &rc, hbrush );
\r
1767 DeleteObject(hbrush);
\r
1770 else if( mode == 2 ) {
\r
1771 /* Diagonal gradient, good more or less for every piece */
\r
1772 POINT triangle[3];
\r
1773 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1774 HBRUSH hbrush_old;
\r
1775 int steps = squareSize;
\r
1778 triangle[0].x = squareSize - steps;
\r
1779 triangle[0].y = squareSize;
\r
1780 triangle[1].x = squareSize;
\r
1781 triangle[1].y = squareSize;
\r
1782 triangle[2].x = squareSize;
\r
1783 triangle[2].y = squareSize - steps;
\r
1785 for( i=0; i<steps; i++ ) {
\r
1786 BYTE r = r1 - (r1-r2) * i / steps;
\r
1787 BYTE g = g1 - (g1-g2) * i / steps;
\r
1788 BYTE b = b1 - (b1-b2) * i / steps;
\r
1790 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1791 hbrush_old = SelectObject( hdc, hbrush );
\r
1792 Polygon( hdc, triangle, 3 );
\r
1793 SelectObject( hdc, hbrush_old );
\r
1794 DeleteObject(hbrush);
\r
1799 SelectObject( hdc, hpen );
\r
1804 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1805 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1806 piece: follow the steps as explained below.
\r
1808 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1812 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1816 int backColor = whitePieceColor;
\r
1817 int foreColor = blackPieceColor;
\r
1819 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1820 backColor = appData.fontBackColorWhite;
\r
1821 foreColor = appData.fontForeColorWhite;
\r
1823 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1824 backColor = appData.fontBackColorBlack;
\r
1825 foreColor = appData.fontForeColorBlack;
\r
1829 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1831 hbm_old = SelectObject( hdc, hbm );
\r
1835 rc.right = squareSize;
\r
1836 rc.bottom = squareSize;
\r
1838 /* Step 1: background is now black */
\r
1839 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1841 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1843 pt.x = (squareSize - sz.cx) / 2;
\r
1844 pt.y = (squareSize - sz.cy) / 2;
\r
1846 SetBkMode( hdc, TRANSPARENT );
\r
1847 SetTextColor( hdc, chroma );
\r
1848 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1849 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1851 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1852 /* Step 3: the area outside the piece is filled with white */
\r
1853 // FloodFill( hdc, 0, 0, chroma );
\r
1854 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1855 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1856 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1857 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1858 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1860 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1861 but if the start point is not inside the piece we're lost!
\r
1862 There should be a better way to do this... if we could create a region or path
\r
1863 from the fill operation we would be fine for example.
\r
1865 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1866 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1868 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1869 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1870 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1872 SelectObject( dc2, bm2 );
\r
1873 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1874 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1875 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1876 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1877 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1880 DeleteObject( bm2 );
\r
1883 SetTextColor( hdc, 0 );
\r
1885 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1886 draw the piece again in black for safety.
\r
1888 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1890 SelectObject( hdc, hbm_old );
\r
1892 if( hPieceMask[index] != NULL ) {
\r
1893 DeleteObject( hPieceMask[index] );
\r
1896 hPieceMask[index] = hbm;
\r
1899 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1901 SelectObject( hdc, hbm );
\r
1904 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1905 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1906 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1908 SelectObject( dc1, hPieceMask[index] );
\r
1909 SelectObject( dc2, bm2 );
\r
1910 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1911 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1914 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1915 the piece background and deletes (makes transparent) the rest.
\r
1916 Thanks to that mask, we are free to paint the background with the greates
\r
1917 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1918 We use this, to make gradients and give the pieces a "roundish" look.
\r
1920 SetPieceBackground( hdc, backColor, 2 );
\r
1921 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1925 DeleteObject( bm2 );
\r
1928 SetTextColor( hdc, foreColor );
\r
1929 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1931 SelectObject( hdc, hbm_old );
\r
1933 if( hPieceFace[index] != NULL ) {
\r
1934 DeleteObject( hPieceFace[index] );
\r
1937 hPieceFace[index] = hbm;
\r
1940 static int TranslatePieceToFontPiece( int piece )
\r
1970 case BlackMarshall:
\r
1974 case BlackNightrider:
\r
1980 case BlackUnicorn:
\r
1984 case BlackGrasshopper:
\r
1996 case BlackCardinal:
\r
2003 case WhiteMarshall:
\r
2007 case WhiteNightrider:
\r
2013 case WhiteUnicorn:
\r
2017 case WhiteGrasshopper:
\r
2029 case WhiteCardinal:
\r
2038 void CreatePiecesFromFont()
\r
2041 HDC hdc_window = NULL;
\r
2047 if( fontBitmapSquareSize < 0 ) {
\r
2048 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
2052 if( !appData.useFont || appData.renderPiecesWithFont == NULL ||
\r
2053 appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
2054 fontBitmapSquareSize = -1;
\r
2058 if( fontBitmapSquareSize != squareSize ) {
\r
2059 hdc_window = GetDC( hwndMain );
\r
2060 hdc = CreateCompatibleDC( hdc_window );
\r
2062 if( hPieceFont != NULL ) {
\r
2063 DeleteObject( hPieceFont );
\r
2066 for( i=0; i<=(int)BlackKing; i++ ) {
\r
2067 hPieceMask[i] = NULL;
\r
2068 hPieceFace[i] = NULL;
\r
2074 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
2075 fontHeight = appData.fontPieceSize;
\r
2078 fontHeight = (fontHeight * squareSize) / 100;
\r
2080 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2082 lf.lfEscapement = 0;
\r
2083 lf.lfOrientation = 0;
\r
2084 lf.lfWeight = FW_NORMAL;
\r
2086 lf.lfUnderline = 0;
\r
2087 lf.lfStrikeOut = 0;
\r
2088 lf.lfCharSet = DEFAULT_CHARSET;
\r
2089 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2090 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2091 lf.lfQuality = PROOF_QUALITY;
\r
2092 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2093 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2094 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2096 hPieceFont = CreateFontIndirect( &lf );
\r
2098 if( hPieceFont == NULL ) {
\r
2099 fontBitmapSquareSize = -2;
\r
2102 /* Setup font-to-piece character table */
\r
2103 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2104 /* No (or wrong) global settings, try to detect the font */
\r
2105 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2107 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2109 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2110 /* DiagramTT* family */
\r
2111 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2113 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2114 /* Fairy symbols */
\r
2115 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2117 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2118 /* Good Companion (Some characters get warped as literal :-( */
\r
2119 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2120 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2121 SetCharTable(pieceToFontChar, s);
\r
2124 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2125 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2129 /* Create bitmaps */
\r
2130 hfont_old = SelectObject( hdc, hPieceFont );
\r
2131 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2132 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2133 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2135 SelectObject( hdc, hfont_old );
\r
2137 fontBitmapSquareSize = squareSize;
\r
2141 if( hdc != NULL ) {
\r
2145 if( hdc_window != NULL ) {
\r
2146 ReleaseDC( hwndMain, hdc_window );
\r
2151 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2153 char name[128], buf[MSG_SIZ];
\r
2155 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2156 if(appData.pieceDirectory[0]) {
\r
2158 snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);
\r
2159 res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
2160 if(res) return res;
\r
2162 if (gameInfo.event &&
\r
2163 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2164 strcmp(name, "k80s") == 0) {
\r
2165 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2167 return LoadBitmap(hinst, name);
\r
2171 /* Insert a color into the program's logical palette
\r
2172 structure. This code assumes the given color is
\r
2173 the result of the RGB or PALETTERGB macro, and it
\r
2174 knows how those macros work (which is documented).
\r
2177 InsertInPalette(COLORREF color)
\r
2179 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2181 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2182 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2183 pLogPal->palNumEntries--;
\r
2187 pe->peFlags = (char) 0;
\r
2188 pe->peRed = (char) (0xFF & color);
\r
2189 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2190 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2196 InitDrawingColors()
\r
2198 if (pLogPal == NULL) {
\r
2199 /* Allocate enough memory for a logical palette with
\r
2200 * PALETTESIZE entries and set the size and version fields
\r
2201 * of the logical palette structure.
\r
2203 pLogPal = (NPLOGPALETTE)
\r
2204 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2205 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2206 pLogPal->palVersion = 0x300;
\r
2208 pLogPal->palNumEntries = 0;
\r
2210 InsertInPalette(lightSquareColor);
\r
2211 InsertInPalette(darkSquareColor);
\r
2212 InsertInPalette(whitePieceColor);
\r
2213 InsertInPalette(blackPieceColor);
\r
2214 InsertInPalette(highlightSquareColor);
\r
2215 InsertInPalette(premoveHighlightColor);
\r
2217 /* create a logical color palette according the information
\r
2218 * in the LOGPALETTE structure.
\r
2220 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2222 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2223 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2224 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2225 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2226 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2227 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2228 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2229 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
2230 /* [AS] Force rendering of the font-based pieces */
\r
2231 if( fontBitmapSquareSize > 0 ) {
\r
2232 fontBitmapSquareSize = 0;
\r
2238 BoardWidth(int boardSize, int n)
\r
2239 { /* [HGM] argument n added to allow different width and height */
\r
2240 int lineGap = sizeInfo[boardSize].lineGap;
\r
2242 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2243 lineGap = appData.overrideLineGap;
\r
2246 return (n + 1) * lineGap +
\r
2247 n * sizeInfo[boardSize].squareSize;
\r
2250 /* Respond to board resize by dragging edge */
\r
2252 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2254 BoardSize newSize = NUM_SIZES - 1;
\r
2255 static int recurse = 0;
\r
2256 if (IsIconic(hwndMain)) return;
\r
2257 if (recurse > 0) return;
\r
2259 while (newSize > 0) {
\r
2260 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2261 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2262 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2265 boardSize = newSize;
\r
2266 InitDrawingSizes(boardSize, flags);
\r
2271 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2274 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2276 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2277 ChessSquare piece;
\r
2278 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2280 SIZE clockSize, messageSize;
\r
2282 char buf[MSG_SIZ];
\r
2284 HMENU hmenu = GetMenu(hwndMain);
\r
2285 RECT crect, wrect, oldRect;
\r
2287 LOGBRUSH logbrush;
\r
2288 VariantClass v = gameInfo.variant;
\r
2290 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2291 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2293 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2294 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2295 if(boardSize == -1) return; // no size defined yet; abort (to allow early call of InitPosition)
\r
2296 oldBoardSize = boardSize;
\r
2298 if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)
\r
2299 { // correct board size to one where built-in pieces exist
\r
2300 if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)
\r
2301 && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range
\r
2302 || (v == VariantShogi && boardSize != SizeModerate) // Japanese-style Shogi
\r
2303 || v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan
\r
2304 || v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy ) {
\r
2305 if(boardSize < SizeMediocre) boardSize = SizePetite; else
\r
2306 if(boardSize > SizeModerate) boardSize = SizeBulky; else
\r
2307 boardSize = SizeMiddling;
\r
2310 if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite
\r
2312 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2313 oldRect.top = wpMain.y;
\r
2314 oldRect.right = wpMain.x + wpMain.width;
\r
2315 oldRect.bottom = wpMain.y + wpMain.height;
\r
2317 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2318 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2319 squareSize = sizeInfo[boardSize].squareSize;
\r
2320 lineGap = sizeInfo[boardSize].lineGap;
\r
2321 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2322 border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;
\r
2324 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2325 lineGap = appData.overrideLineGap;
\r
2328 if (tinyLayout != oldTinyLayout) {
\r
2329 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2331 style &= ~WS_SYSMENU;
\r
2332 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2333 "&Minimize\tCtrl+F4");
\r
2335 style |= WS_SYSMENU;
\r
2336 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2338 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2340 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2341 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2342 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2344 DrawMenuBar(hwndMain);
\r
2347 boardWidth = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;
\r
2348 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;
\r
2350 /* Get text area sizes */
\r
2351 hdc = GetDC(hwndMain);
\r
2352 if (appData.clockMode) {
\r
2353 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2355 snprintf(buf, MSG_SIZ, _("White"));
\r
2357 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2358 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2359 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2360 str = _("We only care about the height here");
\r
2361 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2362 SelectObject(hdc, oldFont);
\r
2363 ReleaseDC(hwndMain, hdc);
\r
2365 /* Compute where everything goes */
\r
2366 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2367 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2368 logoHeight = 2*clockSize.cy;
\r
2369 leftLogoRect.left = OUTER_MARGIN;
\r
2370 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2371 leftLogoRect.top = OUTER_MARGIN;
\r
2372 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2374 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2375 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2376 rightLogoRect.top = OUTER_MARGIN;
\r
2377 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2380 whiteRect.left = leftLogoRect.right;
\r
2381 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2382 whiteRect.top = OUTER_MARGIN;
\r
2383 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2385 blackRect.right = rightLogoRect.left;
\r
2386 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2387 blackRect.top = whiteRect.top;
\r
2388 blackRect.bottom = whiteRect.bottom;
\r
2390 whiteRect.left = OUTER_MARGIN;
\r
2391 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2392 whiteRect.top = OUTER_MARGIN;
\r
2393 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2395 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2396 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2397 blackRect.top = whiteRect.top;
\r
2398 blackRect.bottom = whiteRect.bottom;
\r
2400 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2403 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2404 if (appData.showButtonBar) {
\r
2405 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2406 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2408 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2410 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2411 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2413 boardRect.left = OUTER_MARGIN;
\r
2414 boardRect.right = boardRect.left + boardWidth;
\r
2415 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2416 boardRect.bottom = boardRect.top + boardHeight;
\r
2418 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2419 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2420 oldTinyLayout = tinyLayout;
\r
2421 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2422 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2423 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2424 winW *= 1 + twoBoards;
\r
2425 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2426 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2427 wpMain.height = winH; // without disturbing window attachments
\r
2428 GetWindowRect(hwndMain, &wrect);
\r
2429 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2430 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2432 // [HGM] placement: let attached windows follow size change.
\r
2433 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2434 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2435 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2436 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2437 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2439 /* compensate if menu bar wrapped */
\r
2440 GetClientRect(hwndMain, &crect);
\r
2441 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2442 wpMain.height += offby;
\r
2444 case WMSZ_TOPLEFT:
\r
2445 SetWindowPos(hwndMain, NULL,
\r
2446 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2447 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2450 case WMSZ_TOPRIGHT:
\r
2452 SetWindowPos(hwndMain, NULL,
\r
2453 wrect.left, wrect.bottom - wpMain.height,
\r
2454 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2457 case WMSZ_BOTTOMLEFT:
\r
2459 SetWindowPos(hwndMain, NULL,
\r
2460 wrect.right - wpMain.width, wrect.top,
\r
2461 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2464 case WMSZ_BOTTOMRIGHT:
\r
2468 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2469 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2474 for (i = 0; i < N_BUTTONS; i++) {
\r
2475 if (buttonDesc[i].hwnd != NULL) {
\r
2476 DestroyWindow(buttonDesc[i].hwnd);
\r
2477 buttonDesc[i].hwnd = NULL;
\r
2479 if (appData.showButtonBar) {
\r
2480 buttonDesc[i].hwnd =
\r
2481 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2482 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2483 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2484 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2485 (HMENU) buttonDesc[i].id,
\r
2486 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2488 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2489 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2490 MAKELPARAM(FALSE, 0));
\r
2492 if (buttonDesc[i].id == IDM_Pause)
\r
2493 hwndPause = buttonDesc[i].hwnd;
\r
2494 buttonDesc[i].wndproc = (WNDPROC)
\r
2495 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2498 if (gridPen != NULL) DeleteObject(gridPen);
\r
2499 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2500 if (premovePen != NULL) DeleteObject(premovePen);
\r
2501 if (lineGap != 0) {
\r
2502 logbrush.lbStyle = BS_SOLID;
\r
2503 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2505 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2506 lineGap, &logbrush, 0, NULL);
\r
2507 logbrush.lbColor = highlightSquareColor;
\r
2509 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2510 lineGap, &logbrush, 0, NULL);
\r
2512 logbrush.lbColor = premoveHighlightColor;
\r
2514 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2515 lineGap, &logbrush, 0, NULL);
\r
2517 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2518 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2519 gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;
\r
2520 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2521 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;
\r
2522 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2523 BOARD_WIDTH * (squareSize + lineGap) + border;
\r
2524 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2526 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2527 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;
\r
2528 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2529 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2530 lineGap / 2 + (i * (squareSize + lineGap)) + border;
\r
2531 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2532 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;
\r
2533 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2537 /* [HGM] Licensing requirement */
\r
2539 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2542 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2544 GothicPopUp( "", VariantNormal);
\r
2547 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2549 /* Load piece bitmaps for this board size */
\r
2550 for (i=0; i<=2; i++) {
\r
2551 for (piece = WhitePawn;
\r
2552 (int) piece < (int) BlackPawn;
\r
2553 piece = (ChessSquare) ((int) piece + 1)) {
\r
2554 if (pieceBitmap[i][piece] != NULL)
\r
2555 DeleteObject(pieceBitmap[i][piece]);
\r
2559 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2560 // Orthodox Chess pieces
\r
2561 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2562 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2563 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2564 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2565 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2566 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2567 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2568 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2569 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2570 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2571 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2572 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2573 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2574 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2575 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2576 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2577 // in Shogi, Hijack the unused Queen for Lance
\r
2578 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2579 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2580 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2582 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2583 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2584 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2587 if(squareSize <= 72 && squareSize >= 33) {
\r
2588 /* A & C are available in most sizes now */
\r
2589 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2590 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2591 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2592 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2593 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2594 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2595 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2596 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2597 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2598 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2599 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2600 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2601 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2602 } else { // Smirf-like
\r
2603 if(gameInfo.variant == VariantSChess) {
\r
2604 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2605 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2606 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2608 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2609 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2610 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2613 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2614 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2615 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2616 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2617 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2618 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2619 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2620 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2621 } else { // WinBoard standard
\r
2622 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2623 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2624 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2629 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2630 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2631 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2632 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2633 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2634 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2635 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2636 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2637 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2638 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2639 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2640 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2641 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2642 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2643 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2644 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2645 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2646 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2647 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2648 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2649 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2650 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2651 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2652 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2653 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2654 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2655 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2656 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2657 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2658 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2659 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2661 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2662 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2663 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2664 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2665 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2666 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2667 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2668 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2669 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2670 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2671 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2672 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2673 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2675 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2676 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2677 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2678 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2679 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2680 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2681 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2682 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2683 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2684 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2685 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2686 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2689 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2690 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2691 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2692 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2693 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2694 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2695 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2696 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2697 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2698 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2699 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2700 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2701 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2702 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2703 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2707 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2708 /* special Shogi support in this size */
\r
2709 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2710 for (piece = WhitePawn;
\r
2711 (int) piece < (int) BlackPawn;
\r
2712 piece = (ChessSquare) ((int) piece + 1)) {
\r
2713 if (pieceBitmap[i][piece] != NULL)
\r
2714 DeleteObject(pieceBitmap[i][piece]);
\r
2717 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2718 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2719 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2720 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2721 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2722 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2723 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2724 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2725 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2726 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2727 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2728 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2729 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2730 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2731 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2732 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2733 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2734 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2735 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2736 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2737 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2738 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2739 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2740 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2741 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2742 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2743 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2744 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2745 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2746 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2747 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2748 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2749 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2750 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2751 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2752 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2753 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2754 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2755 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2756 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2757 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2758 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2764 PieceBitmap(ChessSquare p, int kind)
\r
2766 if ((int) p >= (int) BlackPawn)
\r
2767 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2769 return pieceBitmap[kind][(int) p];
\r
2772 /***************************************************************/
\r
2774 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2775 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2777 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2778 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2782 SquareToPos(int row, int column, int * x, int * y)
\r
2785 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
2786 *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;
\r
2788 *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;
\r
2789 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
2794 DrawCoordsOnDC(HDC hdc)
\r
2796 static char files[] = "0123456789012345678901221098765432109876543210";
\r
2797 static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";
\r
2798 char str[2] = { NULLCHAR, NULLCHAR };
\r
2799 int oldMode, oldAlign, x, y, start, i;
\r
2803 if (!appData.showCoords)
\r
2806 start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;
\r
2808 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2809 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2810 oldAlign = GetTextAlign(hdc);
\r
2811 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2813 y = boardRect.top + lineGap;
\r
2814 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2817 SetTextAlign(hdc, TA_RIGHT|TA_TOP);
\r
2818 x += border - lineGap - 4; y += squareSize - 6;
\r
2820 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2821 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2822 str[0] = files[start + i];
\r
2823 ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);
\r
2824 y += squareSize + lineGap;
\r
2827 start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;
\r
2830 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2831 x += -border + 4; y += border - squareSize + 6;
\r
2833 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2834 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2835 str[0] = ranks[start + i];
\r
2836 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2837 x += squareSize + lineGap;
\r
2840 SelectObject(hdc, oldBrush);
\r
2841 SetBkMode(hdc, oldMode);
\r
2842 SetTextAlign(hdc, oldAlign);
\r
2843 SelectObject(hdc, oldFont);
\r
2847 DrawGridOnDC(HDC hdc)
\r
2851 if (lineGap != 0) {
\r
2852 oldPen = SelectObject(hdc, gridPen);
\r
2853 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2854 SelectObject(hdc, oldPen);
\r
2858 #define HIGHLIGHT_PEN 0
\r
2859 #define PREMOVE_PEN 1
\r
2862 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2865 HPEN oldPen, hPen;
\r
2866 if (lineGap == 0) return;
\r
2868 x1 = boardRect.left +
\r
2869 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;
\r
2870 y1 = boardRect.top +
\r
2871 lineGap/2 + y * (squareSize + lineGap) + border;
\r
2873 x1 = boardRect.left +
\r
2874 lineGap/2 + x * (squareSize + lineGap) + border;
\r
2875 y1 = boardRect.top +
\r
2876 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;
\r
2878 hPen = pen ? premovePen : highlightPen;
\r
2879 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2880 MoveToEx(hdc, x1, y1, NULL);
\r
2881 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2882 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2883 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2884 LineTo(hdc, x1, y1);
\r
2885 SelectObject(hdc, oldPen);
\r
2889 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2892 for (i=0; i<2; i++) {
\r
2893 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2894 DrawHighlightOnDC(hdc, TRUE,
\r
2895 h->sq[i].x, h->sq[i].y,
\r
2900 /* Note: sqcolor is used only in monoMode */
\r
2901 /* Note that this code is largely duplicated in woptions.c,
\r
2902 function DrawSampleSquare, so that needs to be updated too */
\r
2904 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2906 HBITMAP oldBitmap;
\r
2910 if (appData.blindfold) return;
\r
2912 /* [AS] Use font-based pieces if needed */
\r
2913 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2914 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2915 CreatePiecesFromFont();
\r
2917 if( fontBitmapSquareSize == squareSize ) {
\r
2918 int index = TranslatePieceToFontPiece(piece);
\r
2920 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2922 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2923 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2927 squareSize, squareSize,
\r
2932 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2934 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2935 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2939 squareSize, squareSize,
\r
2948 if (appData.monoMode) {
\r
2949 SelectObject(tmphdc, PieceBitmap(piece,
\r
2950 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2951 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2952 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2954 HBRUSH xBrush = whitePieceBrush;
\r
2955 tmpSize = squareSize;
\r
2956 if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);
\r
2958 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2959 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2960 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2961 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2962 x += (squareSize - minorSize)>>1;
\r
2963 y += squareSize - minorSize - 2;
\r
2964 tmpSize = minorSize;
\r
2966 if (color || appData.allWhite ) {
\r
2967 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2969 oldBrush = SelectObject(hdc, xBrush);
\r
2970 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2971 if(appData.upsideDown && color==flipView)
\r
2972 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2974 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2975 /* Use black for outline of white pieces */
\r
2976 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2977 if(appData.upsideDown && color==flipView)
\r
2978 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2980 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2981 } else if(appData.pieceDirectory[0]) {
\r
2982 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2983 oldBrush = SelectObject(hdc, xBrush);
\r
2984 if(appData.upsideDown && color==flipView)
\r
2985 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2987 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2988 SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2989 if(appData.upsideDown && color==flipView)
\r
2990 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2992 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2994 /* Use square color for details of black pieces */
\r
2995 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2996 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2997 if(appData.upsideDown && !flipView)
\r
2998 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
3000 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
3002 SelectObject(hdc, oldBrush);
\r
3003 SelectObject(tmphdc, oldBitmap);
\r
3007 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
3008 int GetBackTextureMode( int algo )
\r
3010 int result = BACK_TEXTURE_MODE_DISABLED;
\r
3014 case BACK_TEXTURE_MODE_PLAIN:
\r
3015 result = 1; /* Always use identity map */
\r
3017 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
3018 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
3026 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
3027 to handle redraws cleanly (as random numbers would always be different).
\r
3029 VOID RebuildTextureSquareInfo()
\r
3039 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
3041 if( liteBackTexture != NULL ) {
\r
3042 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3043 lite_w = bi.bmWidth;
\r
3044 lite_h = bi.bmHeight;
\r
3048 if( darkBackTexture != NULL ) {
\r
3049 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3050 dark_w = bi.bmWidth;
\r
3051 dark_h = bi.bmHeight;
\r
3055 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
3056 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
3057 if( (col + row) & 1 ) {
\r
3059 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
3060 if( lite_w >= squareSize*BOARD_WIDTH )
\r
3061 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
3063 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
3064 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
3065 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
3067 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
3068 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
3073 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
3074 if( dark_w >= squareSize*BOARD_WIDTH )
\r
3075 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
3077 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
3078 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
3079 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
3081 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
3082 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
3089 /* [AS] Arrow highlighting support */
\r
3091 static double A_WIDTH = 5; /* Width of arrow body */
\r
3093 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
3094 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
3096 static double Sqr( double x )
\r
3101 static int Round( double x )
\r
3103 return (int) (x + 0.5);
\r
3106 /* Draw an arrow between two points using current settings */
\r
3107 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
3110 double dx, dy, j, k, x, y;
\r
3112 if( d_x == s_x ) {
\r
3113 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3115 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
3118 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
3119 arrow[1].y = d_y - h;
\r
3121 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3122 arrow[2].y = d_y - h;
\r
3127 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3128 arrow[5].y = d_y - h;
\r
3130 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3131 arrow[4].y = d_y - h;
\r
3133 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3136 else if( d_y == s_y ) {
\r
3137 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3140 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3142 arrow[1].x = d_x - w;
\r
3143 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3145 arrow[2].x = d_x - w;
\r
3146 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3151 arrow[5].x = d_x - w;
\r
3152 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3154 arrow[4].x = d_x - w;
\r
3155 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3158 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3161 /* [AS] Needed a lot of paper for this! :-) */
\r
3162 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3163 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3165 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3167 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3172 arrow[0].x = Round(x - j);
\r
3173 arrow[0].y = Round(y + j*dx);
\r
3175 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3176 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3179 x = (double) d_x - k;
\r
3180 y = (double) d_y - k*dy;
\r
3183 x = (double) d_x + k;
\r
3184 y = (double) d_y + k*dy;
\r
3187 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3189 arrow[6].x = Round(x - j);
\r
3190 arrow[6].y = Round(y + j*dx);
\r
3192 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3193 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3195 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3196 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3201 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3202 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3205 Polygon( hdc, arrow, 7 );
\r
3208 /* [AS] Draw an arrow between two squares */
\r
3209 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3211 int s_x, s_y, d_x, d_y;
\r
3218 if( s_col == d_col && s_row == d_row ) {
\r
3222 /* Get source and destination points */
\r
3223 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3224 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3227 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3229 else if( d_y < s_y ) {
\r
3230 d_y += squareSize / 2 + squareSize / 4;
\r
3233 d_y += squareSize / 2;
\r
3237 d_x += squareSize / 2 - squareSize / 4;
\r
3239 else if( d_x < s_x ) {
\r
3240 d_x += squareSize / 2 + squareSize / 4;
\r
3243 d_x += squareSize / 2;
\r
3246 s_x += squareSize / 2;
\r
3247 s_y += squareSize / 2;
\r
3249 /* Adjust width */
\r
3250 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3253 stLB.lbStyle = BS_SOLID;
\r
3254 stLB.lbColor = appData.highlightArrowColor;
\r
3257 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3258 holdpen = SelectObject( hdc, hpen );
\r
3259 hbrush = CreateBrushIndirect( &stLB );
\r
3260 holdbrush = SelectObject( hdc, hbrush );
\r
3262 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3264 SelectObject( hdc, holdpen );
\r
3265 SelectObject( hdc, holdbrush );
\r
3266 DeleteObject( hpen );
\r
3267 DeleteObject( hbrush );
\r
3270 BOOL HasHighlightInfo()
\r
3272 BOOL result = FALSE;
\r
3274 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3275 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3283 BOOL IsDrawArrowEnabled()
\r
3285 BOOL result = FALSE;
\r
3287 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3294 VOID DrawArrowHighlight( HDC hdc )
\r
3296 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3297 DrawArrowBetweenSquares( hdc,
\r
3298 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3299 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3303 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3305 HRGN result = NULL;
\r
3307 if( HasHighlightInfo() ) {
\r
3308 int x1, y1, x2, y2;
\r
3309 int sx, sy, dx, dy;
\r
3311 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3312 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3314 sx = MIN( x1, x2 );
\r
3315 sy = MIN( y1, y2 );
\r
3316 dx = MAX( x1, x2 ) + squareSize;
\r
3317 dy = MAX( y1, y2 ) + squareSize;
\r
3319 result = CreateRectRgn( sx, sy, dx, dy );
\r
3326 Warning: this function modifies the behavior of several other functions.
\r
3328 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3329 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3330 repaint is scattered all over the place, which is not good for features such as
\r
3331 "arrow highlighting" that require a full repaint of the board.
\r
3333 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3334 user interaction, when speed is not so important) but especially to avoid errors
\r
3335 in the displayed graphics.
\r
3337 In such patched places, I always try refer to this function so there is a single
\r
3338 place to maintain knowledge.
\r
3340 To restore the original behavior, just return FALSE unconditionally.
\r
3342 BOOL IsFullRepaintPreferrable()
\r
3344 BOOL result = FALSE;
\r
3346 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3347 /* Arrow may appear on the board */
\r
3355 This function is called by DrawPosition to know whether a full repaint must
\r
3358 Only DrawPosition may directly call this function, which makes use of
\r
3359 some state information. Other function should call DrawPosition specifying
\r
3360 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3362 BOOL DrawPositionNeedsFullRepaint()
\r
3364 BOOL result = FALSE;
\r
3367 Probably a slightly better policy would be to trigger a full repaint
\r
3368 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3369 but animation is fast enough that it's difficult to notice.
\r
3371 if( animInfo.piece == EmptySquare ) {
\r
3372 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3380 static HBITMAP borderBitmap;
\r
3383 DrawBackgroundOnDC(HDC hdc)
\r
3389 static char oldBorder[MSG_SIZ];
\r
3390 int w = 600, h = 600, mode;
\r
3392 if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid
\r
3393 strncpy(oldBorder, appData.border, MSG_SIZ-1);
\r
3394 borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
3396 if(borderBitmap == NULL) { // loading failed, use white
\r
3397 FillRect( hdc, &boardRect, whitePieceBrush );
\r
3400 tmphdc = CreateCompatibleDC(hdc);
\r
3401 hbm = SelectObject(tmphdc, borderBitmap);
\r
3402 if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {
\r
3406 mode = SetStretchBltMode(hdc, COLORONCOLOR);
\r
3407 StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left,
\r
3408 boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3409 SetStretchBltMode(hdc, mode);
\r
3410 SelectObject(tmphdc, hbm);
\r
3415 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3417 int row, column, x, y, square_color, piece_color;
\r
3418 ChessSquare piece;
\r
3420 HDC texture_hdc = NULL;
\r
3422 /* [AS] Initialize background textures if needed */
\r
3423 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3424 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3425 if( backTextureSquareSize != squareSize
\r
3426 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3427 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3428 backTextureSquareSize = squareSize;
\r
3429 RebuildTextureSquareInfo();
\r
3432 texture_hdc = CreateCompatibleDC( hdc );
\r
3435 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3436 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3438 SquareToPos(row, column, &x, &y);
\r
3440 piece = board[row][column];
\r
3442 square_color = ((column + row) % 2) == 1;
\r
3443 if( gameInfo.variant == VariantXiangqi ) {
\r
3444 square_color = !InPalace(row, column);
\r
3445 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3446 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3448 piece_color = (int) piece < (int) BlackPawn;
\r
3451 /* [HGM] holdings file: light square or black */
\r
3452 if(column == BOARD_LEFT-2) {
\r
3453 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3456 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3460 if(column == BOARD_RGHT + 1 ) {
\r
3461 if( row < gameInfo.holdingsSize )
\r
3464 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3468 if(column == BOARD_LEFT-1 ) /* left align */
\r
3469 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3470 else if( column == BOARD_RGHT) /* right align */
\r
3471 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3473 if (appData.monoMode) {
\r
3474 if (piece == EmptySquare) {
\r
3475 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3476 square_color ? WHITENESS : BLACKNESS);
\r
3478 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3481 else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {
\r
3482 /* [AS] Draw the square using a texture bitmap */
\r
3483 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3484 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3485 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3488 squareSize, squareSize,
\r
3491 backTextureSquareInfo[r][c].mode,
\r
3492 backTextureSquareInfo[r][c].x,
\r
3493 backTextureSquareInfo[r][c].y );
\r
3495 SelectObject( texture_hdc, hbm );
\r
3497 if (piece != EmptySquare) {
\r
3498 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3502 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3504 oldBrush = SelectObject(hdc, brush );
\r
3505 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3506 SelectObject(hdc, oldBrush);
\r
3507 if (piece != EmptySquare)
\r
3508 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3513 if( texture_hdc != NULL ) {
\r
3514 DeleteDC( texture_hdc );
\r
3518 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3519 void fputDW(FILE *f, int x)
\r
3521 fputc(x & 255, f);
\r
3522 fputc(x>>8 & 255, f);
\r
3523 fputc(x>>16 & 255, f);
\r
3524 fputc(x>>24 & 255, f);
\r
3527 #define MAX_CLIPS 200 /* more than enough */
\r
3530 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3532 // HBITMAP bufferBitmap;
\r
3537 int w = 100, h = 50;
\r
3539 if(logo == NULL) {
\r
3540 if(!logoHeight) return;
\r
3541 FillRect( hdc, &logoRect, whitePieceBrush );
\r
3543 // GetClientRect(hwndMain, &Rect);
\r
3544 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3545 // Rect.bottom-Rect.top+1);
\r
3546 tmphdc = CreateCompatibleDC(hdc);
\r
3547 hbm = SelectObject(tmphdc, logo);
\r
3548 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3552 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3553 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3554 SelectObject(tmphdc, hbm);
\r
3562 HDC hdc = GetDC(hwndMain);
\r
3563 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3564 if(appData.autoLogo) {
\r
3566 switch(gameMode) { // pick logos based on game mode
\r
3567 case IcsObserving:
\r
3568 whiteLogo = second.programLogo; // ICS logo
\r
3569 blackLogo = second.programLogo;
\r
3572 case IcsPlayingWhite:
\r
3573 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3574 blackLogo = second.programLogo; // ICS logo
\r
3576 case IcsPlayingBlack:
\r
3577 whiteLogo = second.programLogo; // ICS logo
\r
3578 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3580 case TwoMachinesPlay:
\r
3581 if(first.twoMachinesColor[0] == 'b') {
\r
3582 whiteLogo = second.programLogo;
\r
3583 blackLogo = first.programLogo;
\r
3586 case MachinePlaysWhite:
\r
3587 blackLogo = userLogo;
\r
3589 case MachinePlaysBlack:
\r
3590 whiteLogo = userLogo;
\r
3591 blackLogo = first.programLogo;
\r
3594 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3595 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3596 ReleaseDC(hwndMain, hdc);
\r
3601 UpdateLogos(int display)
\r
3602 { // called after loading new engine(s), in tourney or from menu
\r
3603 LoadLogo(&first, 0, FALSE);
\r
3604 LoadLogo(&second, 1, appData.icsActive);
\r
3605 InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos
\r
3606 if(display) DisplayLogos();
\r
3609 static HDC hdcSeek;
\r
3611 // [HGM] seekgraph
\r
3612 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3615 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3616 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3617 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3618 SelectObject( hdcSeek, hp );
\r
3621 // front-end wrapper for drawing functions to do rectangles
\r
3622 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3627 if (hdcSeek == NULL) {
\r
3628 hdcSeek = GetDC(hwndMain);
\r
3629 if (!appData.monoMode) {
\r
3630 SelectPalette(hdcSeek, hPal, FALSE);
\r
3631 RealizePalette(hdcSeek);
\r
3634 hp = SelectObject( hdcSeek, gridPen );
\r
3635 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3636 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3637 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3638 SelectObject( hdcSeek, hp );
\r
3641 // front-end wrapper for putting text in graph
\r
3642 void DrawSeekText(char *buf, int x, int y)
\r
3645 SetBkMode( hdcSeek, TRANSPARENT );
\r
3646 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3647 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3650 void DrawSeekDot(int x, int y, int color)
\r
3652 int square = color & 0x80;
\r
3653 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3654 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3657 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3658 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3660 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3661 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3662 SelectObject(hdcSeek, oldBrush);
\r
3665 void DrawSeekOpen()
\r
3669 void DrawSeekClose()
\r
3674 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3676 static Board lastReq[2], lastDrawn[2];
\r
3677 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3678 static int lastDrawnFlipView = 0;
\r
3679 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3680 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3683 HBITMAP bufferBitmap;
\r
3684 HBITMAP oldBitmap;
\r
3686 HRGN clips[MAX_CLIPS];
\r
3687 ChessSquare dragged_piece = EmptySquare;
\r
3688 int nr = twoBoards*partnerUp;
\r
3690 /* I'm undecided on this - this function figures out whether a full
\r
3691 * repaint is necessary on its own, so there's no real reason to have the
\r
3692 * caller tell it that. I think this can safely be set to FALSE - but
\r
3693 * if we trust the callers not to request full repaints unnessesarily, then
\r
3694 * we could skip some clipping work. In other words, only request a full
\r
3695 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3696 * gamestart and similar) --Hawk
\r
3698 Boolean fullrepaint = repaint;
\r
3700 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3702 if( DrawPositionNeedsFullRepaint() ) {
\r
3703 fullrepaint = TRUE;
\r
3706 if (board == NULL) {
\r
3707 if (!lastReqValid[nr]) {
\r
3710 board = lastReq[nr];
\r
3712 CopyBoard(lastReq[nr], board);
\r
3713 lastReqValid[nr] = 1;
\r
3716 if (doingSizing) {
\r
3720 if (IsIconic(hwndMain)) {
\r
3724 if (hdc == NULL) {
\r
3725 hdc = GetDC(hwndMain);
\r
3726 if (!appData.monoMode) {
\r
3727 SelectPalette(hdc, hPal, FALSE);
\r
3728 RealizePalette(hdc);
\r
3732 releaseDC = FALSE;
\r
3735 /* Create some work-DCs */
\r
3736 hdcmem = CreateCompatibleDC(hdc);
\r
3737 tmphdc = CreateCompatibleDC(hdc);
\r
3739 /* If dragging is in progress, we temporarely remove the piece */
\r
3740 /* [HGM] or temporarily decrease count if stacked */
\r
3741 /* !! Moved to before board compare !! */
\r
3742 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3743 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3744 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3745 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3746 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3748 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3749 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3750 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3752 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3755 /* Figure out which squares need updating by comparing the
\r
3756 * newest board with the last drawn board and checking if
\r
3757 * flipping has changed.
\r
3759 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3760 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3761 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3762 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3763 SquareToPos(row, column, &x, &y);
\r
3764 clips[num_clips++] =
\r
3765 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3769 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3770 for (i=0; i<2; i++) {
\r
3771 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3772 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3773 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3774 lastDrawnHighlight.sq[i].y >= 0) {
\r
3775 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3776 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3777 clips[num_clips++] =
\r
3778 CreateRectRgn(x - lineGap, y - lineGap,
\r
3779 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3781 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3782 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3783 clips[num_clips++] =
\r
3784 CreateRectRgn(x - lineGap, y - lineGap,
\r
3785 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3789 for (i=0; i<2; i++) {
\r
3790 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3791 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3792 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3793 lastDrawnPremove.sq[i].y >= 0) {
\r
3794 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3795 lastDrawnPremove.sq[i].x, &x, &y);
\r
3796 clips[num_clips++] =
\r
3797 CreateRectRgn(x - lineGap, y - lineGap,
\r
3798 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3800 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3801 premoveHighlightInfo.sq[i].y >= 0) {
\r
3802 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3803 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3804 clips[num_clips++] =
\r
3805 CreateRectRgn(x - lineGap, y - lineGap,
\r
3806 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3810 } else { // nr == 1
\r
3811 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3812 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3813 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3814 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3815 for (i=0; i<2; i++) {
\r
3816 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3817 partnerHighlightInfo.sq[i].y >= 0) {
\r
3818 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3819 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3820 clips[num_clips++] =
\r
3821 CreateRectRgn(x - lineGap, y - lineGap,
\r
3822 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3824 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3825 oldPartnerHighlight.sq[i].y >= 0) {
\r
3826 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3827 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3828 clips[num_clips++] =
\r
3829 CreateRectRgn(x - lineGap, y - lineGap,
\r
3830 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3835 fullrepaint = TRUE;
\r
3838 /* Create a buffer bitmap - this is the actual bitmap
\r
3839 * being written to. When all the work is done, we can
\r
3840 * copy it to the real DC (the screen). This avoids
\r
3841 * the problems with flickering.
\r
3843 GetClientRect(hwndMain, &Rect);
\r
3844 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3845 Rect.bottom-Rect.top+1);
\r
3846 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3847 if (!appData.monoMode) {
\r
3848 SelectPalette(hdcmem, hPal, FALSE);
\r
3851 /* Create clips for dragging */
\r
3852 if (!fullrepaint) {
\r
3853 if (dragInfo.from.x >= 0) {
\r
3854 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3855 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3857 if (dragInfo.start.x >= 0) {
\r
3858 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3859 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3861 if (dragInfo.pos.x >= 0) {
\r
3862 x = dragInfo.pos.x - squareSize / 2;
\r
3863 y = dragInfo.pos.y - squareSize / 2;
\r
3864 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3866 if (dragInfo.lastpos.x >= 0) {
\r
3867 x = dragInfo.lastpos.x - squareSize / 2;
\r
3868 y = dragInfo.lastpos.y - squareSize / 2;
\r
3869 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3873 /* Are we animating a move?
\r
3875 * - remove the piece from the board (temporarely)
\r
3876 * - calculate the clipping region
\r
3878 if (!fullrepaint) {
\r
3879 if (animInfo.piece != EmptySquare) {
\r
3880 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3881 x = boardRect.left + animInfo.lastpos.x;
\r
3882 y = boardRect.top + animInfo.lastpos.y;
\r
3883 x2 = boardRect.left + animInfo.pos.x;
\r
3884 y2 = boardRect.top + animInfo.pos.y;
\r
3885 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3886 /* Slight kludge. The real problem is that after AnimateMove is
\r
3887 done, the position on the screen does not match lastDrawn.
\r
3888 This currently causes trouble only on e.p. captures in
\r
3889 atomic, where the piece moves to an empty square and then
\r
3890 explodes. The old and new positions both had an empty square
\r
3891 at the destination, but animation has drawn a piece there and
\r
3892 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3893 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3897 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3898 if (num_clips == 0)
\r
3899 fullrepaint = TRUE;
\r
3901 /* Set clipping on the memory DC */
\r
3902 if (!fullrepaint) {
\r
3903 SelectClipRgn(hdcmem, clips[0]);
\r
3904 for (x = 1; x < num_clips; x++) {
\r
3905 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3906 abort(); // this should never ever happen!
\r
3910 /* Do all the drawing to the memory DC */
\r
3911 if(explodeInfo.radius) { // [HGM] atomic
\r
3913 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3914 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3915 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3916 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3917 x += squareSize/2;
\r
3918 y += squareSize/2;
\r
3919 if(!fullrepaint) {
\r
3920 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3921 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3923 DrawGridOnDC(hdcmem);
\r
3924 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3925 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3926 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3927 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3928 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3929 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3930 SelectObject(hdcmem, oldBrush);
\r
3932 if(border) DrawBackgroundOnDC(hdcmem);
\r
3933 DrawGridOnDC(hdcmem);
\r
3934 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3935 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3936 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3938 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3939 oldPartnerHighlight = partnerHighlightInfo;
\r
3941 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3943 if(nr == 0) // [HGM] dual: markers only on left board
\r
3944 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3945 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3946 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3947 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3948 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3949 SquareToPos(row, column, &x, &y);
\r
3950 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3951 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3952 SelectObject(hdcmem, oldBrush);
\r
3957 if( appData.highlightMoveWithArrow ) {
\r
3958 DrawArrowHighlight(hdcmem);
\r
3961 DrawCoordsOnDC(hdcmem);
\r
3963 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3964 /* to make sure lastDrawn contains what is actually drawn */
\r
3966 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3967 if (dragged_piece != EmptySquare) {
\r
3968 /* [HGM] or restack */
\r
3969 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3970 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3972 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3973 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3974 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3975 x = dragInfo.pos.x - squareSize / 2;
\r
3976 y = dragInfo.pos.y - squareSize / 2;
\r
3977 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
3978 ((int) dragInfo.piece < (int) BlackPawn),
\r
3979 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3982 /* Put the animated piece back into place and draw it */
\r
3983 if (animInfo.piece != EmptySquare) {
\r
3984 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3985 x = boardRect.left + animInfo.pos.x;
\r
3986 y = boardRect.top + animInfo.pos.y;
\r
3987 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3988 ((int) animInfo.piece < (int) BlackPawn),
\r
3989 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3992 /* Release the bufferBitmap by selecting in the old bitmap
\r
3993 * and delete the memory DC
\r
3995 SelectObject(hdcmem, oldBitmap);
\r
3998 /* Set clipping on the target DC */
\r
3999 if (!fullrepaint) {
\r
4000 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
4002 GetRgnBox(clips[x], &rect);
\r
4003 DeleteObject(clips[x]);
\r
4004 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
4005 rect.right + wpMain.width/2, rect.bottom);
\r
4007 SelectClipRgn(hdc, clips[0]);
\r
4008 for (x = 1; x < num_clips; x++) {
\r
4009 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
4010 abort(); // this should never ever happen!
\r
4014 /* Copy the new bitmap onto the screen in one go.
\r
4015 * This way we avoid any flickering
\r
4017 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
4018 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
4019 boardRect.right - boardRect.left,
\r
4020 boardRect.bottom - boardRect.top,
\r
4021 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
4022 if(saveDiagFlag) {
\r
4023 BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData;
\r
4024 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
4026 GetObject(bufferBitmap, sizeof(b), &b);
\r
4027 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\r
4028 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
4029 bih.biWidth = b.bmWidth;
\r
4030 bih.biHeight = b.bmHeight;
\r
4032 bih.biBitCount = b.bmBitsPixel;
\r
4033 bih.biCompression = 0;
\r
4034 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
4035 bih.biXPelsPerMeter = 0;
\r
4036 bih.biYPelsPerMeter = 0;
\r
4037 bih.biClrUsed = 0;
\r
4038 bih.biClrImportant = 0;
\r
4039 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
4040 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
4041 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
4042 // fprintf(diagFile, "%8x\n", (int) pData);
\r
4044 wb = b.bmWidthBytes;
\r
4046 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
4047 int k = ((int*) pData)[i];
\r
4048 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4049 if(j >= 16) break;
\r
4051 if(j >= nrColors) nrColors = j+1;
\r
4053 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
4055 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
4056 for(w=0; w<(wb>>2); w+=2) {
\r
4057 int k = ((int*) pData)[(wb*i>>2) + w];
\r
4058 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4059 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
4060 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
4061 pData[p++] = m | j<<4;
\r
4063 while(p&3) pData[p++] = 0;
\r
4066 wb = ((wb+31)>>5)<<2;
\r
4068 // write BITMAPFILEHEADER
\r
4069 fprintf(diagFile, "BM");
\r
4070 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
4071 fputDW(diagFile, 0);
\r
4072 fputDW(diagFile, 0x36 + (fac?64:0));
\r
4073 // write BITMAPINFOHEADER
\r
4074 fputDW(diagFile, 40);
\r
4075 fputDW(diagFile, b.bmWidth);
\r
4076 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
4077 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
4078 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
4079 fputDW(diagFile, 0);
\r
4080 fputDW(diagFile, 0);
\r
4081 fputDW(diagFile, 0);
\r
4082 fputDW(diagFile, 0);
\r
4083 fputDW(diagFile, 0);
\r
4084 fputDW(diagFile, 0);
\r
4085 // write color table
\r
4087 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
4088 // write bitmap data
\r
4089 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
4090 fputc(pData[i], diagFile);
\r
4095 SelectObject(tmphdc, oldBitmap);
\r
4097 /* Massive cleanup */
\r
4098 for (x = 0; x < num_clips; x++)
\r
4099 DeleteObject(clips[x]);
\r
4102 DeleteObject(bufferBitmap);
\r
4105 ReleaseDC(hwndMain, hdc);
\r
4107 if (lastDrawnFlipView != flipView && nr == 0) {
\r
4109 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
4111 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
4114 /* CopyBoard(lastDrawn, board);*/
\r
4115 lastDrawnHighlight = highlightInfo;
\r
4116 lastDrawnPremove = premoveHighlightInfo;
\r
4117 lastDrawnFlipView = flipView;
\r
4118 lastDrawnValid[nr] = 1;
\r
4121 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
4126 saveDiagFlag = 1; diagFile = f;
\r
4127 HDCDrawPosition(NULL, TRUE, NULL);
\r
4135 /*---------------------------------------------------------------------------*\
\r
4136 | CLIENT PAINT PROCEDURE
\r
4137 | This is the main event-handler for the WM_PAINT message.
\r
4139 \*---------------------------------------------------------------------------*/
\r
4141 PaintProc(HWND hwnd)
\r
4147 if((hdc = BeginPaint(hwnd, &ps))) {
\r
4148 if (IsIconic(hwnd)) {
\r
4149 DrawIcon(hdc, 2, 2, iconCurrent);
\r
4151 if (!appData.monoMode) {
\r
4152 SelectPalette(hdc, hPal, FALSE);
\r
4153 RealizePalette(hdc);
\r
4155 HDCDrawPosition(hdc, 1, NULL);
\r
4156 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
4157 flipView = !flipView; partnerUp = !partnerUp;
\r
4158 HDCDrawPosition(hdc, 1, NULL);
\r
4159 flipView = !flipView; partnerUp = !partnerUp;
\r
4162 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
4163 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
4164 ETO_CLIPPED|ETO_OPAQUE,
\r
4165 &messageRect, messageText, strlen(messageText), NULL);
\r
4166 SelectObject(hdc, oldFont);
\r
4167 DisplayBothClocks();
\r
4170 EndPaint(hwnd,&ps);
\r
4178 * If the user selects on a border boundary, return -1; if off the board,
\r
4179 * return -2. Otherwise map the event coordinate to the square.
\r
4180 * The offset boardRect.left or boardRect.top must already have been
\r
4181 * subtracted from x.
\r
4183 int EventToSquare(x, limit)
\r
4188 if (x < lineGap + border)
\r
4190 x -= lineGap + border;
\r
4191 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4193 x /= (squareSize + lineGap);
\r
4205 DropEnable dropEnables[] = {
\r
4206 { 'P', DP_Pawn, N_("Pawn") },
\r
4207 { 'N', DP_Knight, N_("Knight") },
\r
4208 { 'B', DP_Bishop, N_("Bishop") },
\r
4209 { 'R', DP_Rook, N_("Rook") },
\r
4210 { 'Q', DP_Queen, N_("Queen") },
\r
4214 SetupDropMenu(HMENU hmenu)
\r
4216 int i, count, enable;
\r
4218 extern char white_holding[], black_holding[];
\r
4219 char item[MSG_SIZ];
\r
4221 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4222 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4223 dropEnables[i].piece);
\r
4225 while (p && *p++ == dropEnables[i].piece) count++;
\r
4226 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4227 enable = count > 0 || !appData.testLegality
\r
4228 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4229 && !appData.icsActive);
\r
4230 ModifyMenu(hmenu, dropEnables[i].command,
\r
4231 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4232 dropEnables[i].command, item);
\r
4236 void DragPieceBegin(int x, int y, Boolean instantly)
\r
4238 dragInfo.lastpos.x = boardRect.left + x;
\r
4239 dragInfo.lastpos.y = boardRect.top + y;
\r
4240 if(instantly) dragInfo.pos = dragInfo.lastpos;
\r
4241 dragInfo.from.x = fromX;
\r
4242 dragInfo.from.y = fromY;
\r
4243 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4244 dragInfo.start = dragInfo.from;
\r
4245 SetCapture(hwndMain);
\r
4248 void DragPieceEnd(int x, int y)
\r
4251 dragInfo.start.x = dragInfo.start.y = -1;
\r
4252 dragInfo.from = dragInfo.start;
\r
4253 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4256 void ChangeDragPiece(ChessSquare piece)
\r
4258 dragInfo.piece = piece;
\r
4261 /* Event handler for mouse messages */
\r
4263 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4267 static int recursive = 0;
\r
4269 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4272 if (message == WM_MBUTTONUP) {
\r
4273 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4274 to the middle button: we simulate pressing the left button too!
\r
4276 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4277 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4283 pt.x = LOWORD(lParam);
\r
4284 pt.y = HIWORD(lParam);
\r
4285 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4286 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4287 if (!flipView && y >= 0) {
\r
4288 y = BOARD_HEIGHT - 1 - y;
\r
4290 if (flipView && x >= 0) {
\r
4291 x = BOARD_WIDTH - 1 - x;
\r
4294 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4295 controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status
\r
4297 switch (message) {
\r
4298 case WM_LBUTTONDOWN:
\r
4299 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4300 ClockClick(flipClock); break;
\r
4301 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4302 ClockClick(!flipClock); break;
\r
4304 dragInfo.start.x = dragInfo.start.y = -1;
\r
4305 dragInfo.from = dragInfo.start;
\r
4306 if(fromX == -1 && frozen) { // not sure where this is for
\r
4307 fromX = fromY = -1;
\r
4308 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4311 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4312 DrawPosition(TRUE, NULL);
\r
4315 case WM_LBUTTONUP:
\r
4316 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4317 DrawPosition(TRUE, NULL);
\r
4320 case WM_MOUSEMOVE:
\r
4321 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4322 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4323 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4324 if ((appData.animateDragging || appData.highlightDragging)
\r
4325 && (wParam & MK_LBUTTON)
\r
4326 && dragInfo.from.x >= 0)
\r
4328 BOOL full_repaint = FALSE;
\r
4330 if (appData.animateDragging) {
\r
4331 dragInfo.pos = pt;
\r
4333 if (appData.highlightDragging) {
\r
4334 SetHighlights(fromX, fromY, x, y);
\r
4335 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4336 full_repaint = TRUE;
\r
4340 DrawPosition( full_repaint, NULL);
\r
4342 dragInfo.lastpos = dragInfo.pos;
\r
4346 case WM_MOUSEWHEEL: // [DM]
\r
4347 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4348 /* Mouse Wheel is being rolled forward
\r
4349 * Play moves forward
\r
4351 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4352 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4353 /* Mouse Wheel is being rolled backward
\r
4354 * Play moves backward
\r
4356 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4357 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4361 case WM_MBUTTONUP:
\r
4362 case WM_RBUTTONUP:
\r
4364 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4367 case WM_MBUTTONDOWN:
\r
4368 case WM_RBUTTONDOWN:
\r
4371 fromX = fromY = -1;
\r
4372 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4373 dragInfo.start.x = dragInfo.start.y = -1;
\r
4374 dragInfo.from = dragInfo.start;
\r
4375 dragInfo.lastpos = dragInfo.pos;
\r
4376 if (appData.highlightDragging) {
\r
4377 ClearHighlights();
\r
4380 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4381 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4382 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4383 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4384 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4388 DrawPosition(TRUE, NULL);
\r
4390 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4393 if (message == WM_MBUTTONDOWN) {
\r
4394 buttonCount = 3; /* even if system didn't think so */
\r
4395 if (wParam & MK_SHIFT)
\r
4396 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4398 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4399 } else { /* message == WM_RBUTTONDOWN */
\r
4400 /* Just have one menu, on the right button. Windows users don't
\r
4401 think to try the middle one, and sometimes other software steals
\r
4402 it, or it doesn't really exist. */
\r
4403 if(gameInfo.variant != VariantShogi)
\r
4404 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4406 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4410 SetCapture(hwndMain);
\r
4413 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4414 SetupDropMenu(hmenu);
\r
4415 MenuPopup(hwnd, pt, hmenu, -1);
\r
4425 /* Preprocess messages for buttons in main window */
\r
4427 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4429 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4432 for (i=0; i<N_BUTTONS; i++) {
\r
4433 if (buttonDesc[i].id == id) break;
\r
4435 if (i == N_BUTTONS) return 0;
\r
4436 switch (message) {
\r
4441 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4442 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4449 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4452 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4453 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4454 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4455 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4457 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4459 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4460 TypeInEvent((char)wParam);
\r
4466 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4469 /* Process messages for Promotion dialog box */
\r
4471 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4475 switch (message) {
\r
4476 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4477 /* Center the dialog over the application window */
\r
4478 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4479 Translate(hDlg, DLG_PromotionKing);
\r
4480 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4481 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4482 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4483 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4484 SW_SHOW : SW_HIDE);
\r
4485 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4486 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4487 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4488 PieceToChar(WhiteAngel) != '~') ||
\r
4489 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4490 PieceToChar(BlackAngel) != '~') ) ?
\r
4491 SW_SHOW : SW_HIDE);
\r
4492 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4493 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4494 PieceToChar(WhiteMarshall) != '~') ||
\r
4495 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4496 PieceToChar(BlackMarshall) != '~') ) ?
\r
4497 SW_SHOW : SW_HIDE);
\r
4498 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4499 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4500 gameInfo.variant != VariantShogi ?
\r
4501 SW_SHOW : SW_HIDE);
\r
4502 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4503 gameInfo.variant != VariantShogi ?
\r
4504 SW_SHOW : SW_HIDE);
\r
4505 if(gameInfo.variant == VariantShogi) {
\r
4506 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4507 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4508 SetWindowText(hDlg, "Promote?");
\r
4510 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4511 gameInfo.variant == VariantSuper ?
\r
4512 SW_SHOW : SW_HIDE);
\r
4515 case WM_COMMAND: /* message: received a command */
\r
4516 switch (LOWORD(wParam)) {
\r
4518 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4519 ClearHighlights();
\r
4520 DrawPosition(FALSE, NULL);
\r
4523 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4526 promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4529 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4530 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4533 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4534 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4536 case PB_Chancellor:
\r
4537 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4539 case PB_Archbishop:
\r
4540 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4543 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);
\r
4548 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4549 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4550 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4551 fromX = fromY = -1;
\r
4552 if (!appData.highlightLastMove) {
\r
4553 ClearHighlights();
\r
4554 DrawPosition(FALSE, NULL);
\r
4561 /* Pop up promotion dialog */
\r
4563 PromotionPopup(HWND hwnd)
\r
4567 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4568 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4569 hwnd, (DLGPROC)lpProc);
\r
4570 FreeProcInstance(lpProc);
\r
4576 DrawPosition(TRUE, NULL);
\r
4577 PromotionPopup(hwndMain);
\r
4581 LoadGameDialog(HWND hwnd, char* title)
\r
4585 char fileTitle[MSG_SIZ];
\r
4586 f = OpenFileDialog(hwnd, "rb", "",
\r
4587 appData.oldSaveStyle ? "gam" : "pgn",
\r
4589 title, &number, fileTitle, NULL);
\r
4591 cmailMsgLoaded = FALSE;
\r
4592 if (number == 0) {
\r
4593 int error = GameListBuild(f);
\r
4595 DisplayError(_("Cannot build game list"), error);
\r
4596 } else if (!ListEmpty(&gameList) &&
\r
4597 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4598 GameListPopUp(f, fileTitle);
\r
4601 GameListDestroy();
\r
4604 LoadGame(f, number, fileTitle, FALSE);
\r
4608 int get_term_width()
\r
4613 HFONT hfont, hold_font;
\r
4618 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4622 // get the text metrics
\r
4623 hdc = GetDC(hText);
\r
4624 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4625 if (consoleCF.dwEffects & CFE_BOLD)
\r
4626 lf.lfWeight = FW_BOLD;
\r
4627 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4628 lf.lfItalic = TRUE;
\r
4629 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4630 lf.lfStrikeOut = TRUE;
\r
4631 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4632 lf.lfUnderline = TRUE;
\r
4633 hfont = CreateFontIndirect(&lf);
\r
4634 hold_font = SelectObject(hdc, hfont);
\r
4635 GetTextMetrics(hdc, &tm);
\r
4636 SelectObject(hdc, hold_font);
\r
4637 DeleteObject(hfont);
\r
4638 ReleaseDC(hText, hdc);
\r
4640 // get the rectangle
\r
4641 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4643 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4646 void UpdateICSWidth(HWND hText)
\r
4648 LONG old_width, new_width;
\r
4650 new_width = get_term_width(hText, FALSE);
\r
4651 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4652 if (new_width != old_width)
\r
4654 ics_update_width(new_width);
\r
4655 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4660 ChangedConsoleFont()
\r
4663 CHARRANGE tmpsel, sel;
\r
4664 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4665 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4666 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4669 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4670 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4671 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4672 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4673 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4674 * size. This was undocumented in the version of MSVC++ that I had
\r
4675 * when I wrote the code, but is apparently documented now.
\r
4677 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4678 cfmt.bCharSet = f->lf.lfCharSet;
\r
4679 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4680 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4681 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4682 /* Why are the following seemingly needed too? */
\r
4683 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4684 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4685 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4687 tmpsel.cpMax = -1; /*999999?*/
\r
4688 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4689 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4690 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4691 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4693 paraf.cbSize = sizeof(paraf);
\r
4694 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4695 paraf.dxStartIndent = 0;
\r
4696 paraf.dxOffset = WRAP_INDENT;
\r
4697 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4698 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4699 UpdateICSWidth(hText);
\r
4702 /*---------------------------------------------------------------------------*\
\r
4704 * Window Proc for main window
\r
4706 \*---------------------------------------------------------------------------*/
\r
4708 /* Process messages for main window, etc. */
\r
4710 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4713 int wmId, wmEvent;
\r
4717 char fileTitle[MSG_SIZ];
\r
4718 static SnapData sd;
\r
4719 static int peek=0;
\r
4721 switch (message) {
\r
4723 case WM_PAINT: /* message: repaint portion of window */
\r
4727 case WM_ERASEBKGND:
\r
4728 if (IsIconic(hwnd)) {
\r
4729 /* Cheat; change the message */
\r
4730 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4732 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4736 case WM_LBUTTONDOWN:
\r
4737 case WM_MBUTTONDOWN:
\r
4738 case WM_RBUTTONDOWN:
\r
4739 case WM_LBUTTONUP:
\r
4740 case WM_MBUTTONUP:
\r
4741 case WM_RBUTTONUP:
\r
4742 case WM_MOUSEMOVE:
\r
4743 case WM_MOUSEWHEEL:
\r
4744 MouseEvent(hwnd, message, wParam, lParam);
\r
4748 if((char)wParam == '\b') {
\r
4749 ForwardEvent(); peek = 0;
\r
4752 JAWS_KBUP_NAVIGATION
\r
4757 if((char)wParam == '\b') {
\r
4758 if(!peek) BackwardEvent(), peek = 1;
\r
4761 JAWS_KBDOWN_NAVIGATION
\r
4767 JAWS_ALT_INTERCEPT
\r
4769 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4770 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4771 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4772 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4774 SendMessage(h, message, wParam, lParam);
\r
4775 } else if(lParam != KF_REPEAT) {
\r
4776 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4777 TypeInEvent((char)wParam);
\r
4778 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4779 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4784 case WM_PALETTECHANGED:
\r
4785 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4787 HDC hdc = GetDC(hwndMain);
\r
4788 SelectPalette(hdc, hPal, TRUE);
\r
4789 nnew = RealizePalette(hdc);
\r
4791 paletteChanged = TRUE;
\r
4792 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4794 ReleaseDC(hwnd, hdc);
\r
4798 case WM_QUERYNEWPALETTE:
\r
4799 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4801 HDC hdc = GetDC(hwndMain);
\r
4802 paletteChanged = FALSE;
\r
4803 SelectPalette(hdc, hPal, FALSE);
\r
4804 nnew = RealizePalette(hdc);
\r
4806 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4808 ReleaseDC(hwnd, hdc);
\r
4813 case WM_COMMAND: /* message: command from application menu */
\r
4814 wmId = LOWORD(wParam);
\r
4815 wmEvent = HIWORD(wParam);
\r
4820 SAY("new game enter a move to play against the computer with white");
\r
4823 case IDM_NewGameFRC:
\r
4824 if( NewGameFRC() == 0 ) {
\r
4829 case IDM_NewVariant:
\r
4830 NewVariantPopup(hwnd);
\r
4833 case IDM_LoadGame:
\r
4834 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4837 case IDM_LoadNextGame:
\r
4841 case IDM_LoadPrevGame:
\r
4845 case IDM_ReloadGame:
\r
4849 case IDM_LoadPosition:
\r
4850 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4851 Reset(FALSE, TRUE);
\r
4854 f = OpenFileDialog(hwnd, "rb", "",
\r
4855 appData.oldSaveStyle ? "pos" : "fen",
\r
4857 _("Load Position from File"), &number, fileTitle, NULL);
\r
4859 LoadPosition(f, number, fileTitle);
\r
4863 case IDM_LoadNextPosition:
\r
4864 ReloadPosition(1);
\r
4867 case IDM_LoadPrevPosition:
\r
4868 ReloadPosition(-1);
\r
4871 case IDM_ReloadPosition:
\r
4872 ReloadPosition(0);
\r
4875 case IDM_SaveGame:
\r
4876 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4877 f = OpenFileDialog(hwnd, "a", defName,
\r
4878 appData.oldSaveStyle ? "gam" : "pgn",
\r
4880 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4882 SaveGame(f, 0, "");
\r
4886 case IDM_SavePosition:
\r
4887 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4888 f = OpenFileDialog(hwnd, "a", defName,
\r
4889 appData.oldSaveStyle ? "pos" : "fen",
\r
4891 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4893 SavePosition(f, 0, "");
\r
4897 case IDM_SaveDiagram:
\r
4898 defName = "diagram";
\r
4899 f = OpenFileDialog(hwnd, "wb", defName,
\r
4902 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
4908 case IDM_CreateBook:
\r
4909 CreateBookEvent();
\r
4912 case IDM_CopyGame:
\r
4913 CopyGameToClipboard();
\r
4916 case IDM_PasteGame:
\r
4917 PasteGameFromClipboard();
\r
4920 case IDM_CopyGameListToClipboard:
\r
4921 CopyGameListToClipboard();
\r
4924 /* [AS] Autodetect FEN or PGN data */
\r
4925 case IDM_PasteAny:
\r
4926 PasteGameOrFENFromClipboard();
\r
4929 /* [AS] Move history */
\r
4930 case IDM_ShowMoveHistory:
\r
4931 if( MoveHistoryIsUp() ) {
\r
4932 MoveHistoryPopDown();
\r
4935 MoveHistoryPopUp();
\r
4939 /* [AS] Eval graph */
\r
4940 case IDM_ShowEvalGraph:
\r
4941 if( EvalGraphIsUp() ) {
\r
4942 EvalGraphPopDown();
\r
4946 SetFocus(hwndMain);
\r
4950 /* [AS] Engine output */
\r
4951 case IDM_ShowEngineOutput:
\r
4952 if( EngineOutputIsUp() ) {
\r
4953 EngineOutputPopDown();
\r
4956 EngineOutputPopUp();
\r
4960 /* [AS] User adjudication */
\r
4961 case IDM_UserAdjudication_White:
\r
4962 UserAdjudicationEvent( +1 );
\r
4965 case IDM_UserAdjudication_Black:
\r
4966 UserAdjudicationEvent( -1 );
\r
4969 case IDM_UserAdjudication_Draw:
\r
4970 UserAdjudicationEvent( 0 );
\r
4973 /* [AS] Game list options dialog */
\r
4974 case IDM_GameListOptions:
\r
4975 GameListOptions();
\r
4982 case IDM_CopyPosition:
\r
4983 CopyFENToClipboard();
\r
4986 case IDM_PastePosition:
\r
4987 PasteFENFromClipboard();
\r
4990 case IDM_MailMove:
\r
4994 case IDM_ReloadCMailMsg:
\r
4995 Reset(TRUE, TRUE);
\r
4996 ReloadCmailMsgEvent(FALSE);
\r
4999 case IDM_Minimize:
\r
5000 ShowWindow(hwnd, SW_MINIMIZE);
\r
5007 case IDM_MachineWhite:
\r
5008 MachineWhiteEvent();
\r
5010 * refresh the tags dialog only if it's visible
\r
5012 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
5014 tags = PGNTags(&gameInfo);
\r
5015 TagsPopUp(tags, CmailMsg());
\r
5018 SAY("computer starts playing white");
\r
5021 case IDM_MachineBlack:
\r
5022 MachineBlackEvent();
\r
5024 * refresh the tags dialog only if it's visible
\r
5026 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
5028 tags = PGNTags(&gameInfo);
\r
5029 TagsPopUp(tags, CmailMsg());
\r
5032 SAY("computer starts playing black");
\r
5035 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
5036 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
5039 case IDM_TwoMachines:
\r
5040 TwoMachinesEvent();
\r
5042 * refresh the tags dialog only if it's visible
\r
5044 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
5046 tags = PGNTags(&gameInfo);
\r
5047 TagsPopUp(tags, CmailMsg());
\r
5050 SAY("computer starts playing both sides");
\r
5053 case IDM_AnalysisMode:
\r
5054 if(AnalyzeModeEvent()) {
\r
5055 SAY("analyzing current position");
\r
5059 case IDM_AnalyzeFile:
\r
5060 AnalyzeFileEvent();
\r
5063 case IDM_IcsClient:
\r
5067 case IDM_EditGame:
\r
5068 case IDM_EditGame2:
\r
5073 case IDM_EditPosition:
\r
5074 case IDM_EditPosition2:
\r
5075 EditPositionEvent();
\r
5076 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
5079 case IDM_Training:
\r
5083 case IDM_ShowGameList:
\r
5084 ShowGameListProc();
\r
5087 case IDM_EditProgs1:
\r
5088 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
5091 case IDM_LoadProg1:
\r
5092 LoadEnginePopUp(hwndMain, 0);
\r
5095 case IDM_LoadProg2:
\r
5096 LoadEnginePopUp(hwndMain, 1);
\r
5099 case IDM_EditServers:
\r
5100 EditTagsPopUp(icsNames, &icsNames);
\r
5103 case IDM_EditTags:
\r
5108 case IDM_EditBook:
\r
5112 case IDM_EditComment:
\r
5114 if (commentUp && editComment) {
\r
5117 EditCommentEvent();
\r
5137 case IDM_CallFlag:
\r
5157 case IDM_StopObserving:
\r
5158 StopObservingEvent();
\r
5161 case IDM_StopExamining:
\r
5162 StopExaminingEvent();
\r
5166 UploadGameEvent();
\r
5169 case IDM_TypeInMove:
\r
5170 TypeInEvent('\000');
\r
5173 case IDM_TypeInName:
\r
5174 PopUpNameDialog('\000');
\r
5177 case IDM_Backward:
\r
5179 SetFocus(hwndMain);
\r
5186 SetFocus(hwndMain);
\r
5191 SetFocus(hwndMain);
\r
5196 SetFocus(hwndMain);
\r
5199 case OPT_GameListNext: // [HGM] forward these two accelerators to Game List
\r
5200 case OPT_GameListPrev:
\r
5201 if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);
\r
5205 RevertEvent(FALSE);
\r
5208 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5209 RevertEvent(TRUE);
\r
5212 case IDM_TruncateGame:
\r
5213 TruncateGameEvent();
\r
5220 case IDM_RetractMove:
\r
5221 RetractMoveEvent();
\r
5224 case IDM_FlipView:
\r
5225 flipView = !flipView;
\r
5226 DrawPosition(FALSE, NULL);
\r
5229 case IDM_FlipClock:
\r
5230 flipClock = !flipClock;
\r
5231 DisplayBothClocks();
\r
5235 case IDM_MuteSounds:
\r
5236 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5237 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5238 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5241 case IDM_GeneralOptions:
\r
5242 GeneralOptionsPopup(hwnd);
\r
5243 DrawPosition(TRUE, NULL);
\r
5246 case IDM_BoardOptions:
\r
5247 BoardOptionsPopup(hwnd);
\r
5250 case IDM_ThemeOptions:
\r
5251 ThemeOptionsPopup(hwnd);
\r
5254 case IDM_EnginePlayOptions:
\r
5255 EnginePlayOptionsPopup(hwnd);
\r
5258 case IDM_Engine1Options:
\r
5259 EngineOptionsPopup(hwnd, &first);
\r
5262 case IDM_Engine2Options:
\r
5264 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5265 EngineOptionsPopup(hwnd, &second);
\r
5268 case IDM_OptionsUCI:
\r
5269 UciOptionsPopup(hwnd);
\r
5273 TourneyPopup(hwnd);
\r
5276 case IDM_IcsOptions:
\r
5277 IcsOptionsPopup(hwnd);
\r
5281 FontsOptionsPopup(hwnd);
\r
5285 SoundOptionsPopup(hwnd);
\r
5288 case IDM_CommPort:
\r
5289 CommPortOptionsPopup(hwnd);
\r
5292 case IDM_LoadOptions:
\r
5293 LoadOptionsPopup(hwnd);
\r
5296 case IDM_SaveOptions:
\r
5297 SaveOptionsPopup(hwnd);
\r
5300 case IDM_TimeControl:
\r
5301 TimeControlOptionsPopup(hwnd);
\r
5304 case IDM_SaveSettings:
\r
5305 SaveSettings(settingsFileName);
\r
5308 case IDM_SaveSettingsOnExit:
\r
5309 saveSettingsOnExit = !saveSettingsOnExit;
\r
5310 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5311 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5312 MF_CHECKED : MF_UNCHECKED));
\r
5323 case IDM_AboutGame:
\r
5328 appData.debugMode = !appData.debugMode;
\r
5329 if (appData.debugMode) {
\r
5330 char dir[MSG_SIZ];
\r
5331 GetCurrentDirectory(MSG_SIZ, dir);
\r
5332 SetCurrentDirectory(installDir);
\r
5333 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5334 SetCurrentDirectory(dir);
\r
5335 setbuf(debugFP, NULL);
\r
5342 case IDM_HELPCONTENTS:
\r
5343 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5344 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5345 MessageBox (GetFocus(),
\r
5346 _("Unable to activate help"),
\r
5347 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5351 case IDM_HELPSEARCH:
\r
5352 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5353 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5354 MessageBox (GetFocus(),
\r
5355 _("Unable to activate help"),
\r
5356 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5360 case IDM_HELPHELP:
\r
5361 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5362 MessageBox (GetFocus(),
\r
5363 _("Unable to activate help"),
\r
5364 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5369 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5371 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5372 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5373 FreeProcInstance(lpProc);
\r
5376 case IDM_DirectCommand1:
\r
5377 AskQuestionEvent(_("Direct Command"),
\r
5378 _("Send to chess program:"), "", "1");
\r
5380 case IDM_DirectCommand2:
\r
5381 AskQuestionEvent(_("Direct Command"),
\r
5382 _("Send to second chess program:"), "", "2");
\r
5385 case EP_WhitePawn:
\r
5386 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5387 fromX = fromY = -1;
\r
5390 case EP_WhiteKnight:
\r
5391 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5392 fromX = fromY = -1;
\r
5395 case EP_WhiteBishop:
\r
5396 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5397 fromX = fromY = -1;
\r
5400 case EP_WhiteRook:
\r
5401 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5402 fromX = fromY = -1;
\r
5405 case EP_WhiteQueen:
\r
5406 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5407 fromX = fromY = -1;
\r
5410 case EP_WhiteFerz:
\r
5411 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5412 fromX = fromY = -1;
\r
5415 case EP_WhiteWazir:
\r
5416 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5417 fromX = fromY = -1;
\r
5420 case EP_WhiteAlfil:
\r
5421 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5422 fromX = fromY = -1;
\r
5425 case EP_WhiteCannon:
\r
5426 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5427 fromX = fromY = -1;
\r
5430 case EP_WhiteCardinal:
\r
5431 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5432 fromX = fromY = -1;
\r
5435 case EP_WhiteMarshall:
\r
5436 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5437 fromX = fromY = -1;
\r
5440 case EP_WhiteKing:
\r
5441 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5442 fromX = fromY = -1;
\r
5445 case EP_BlackPawn:
\r
5446 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5447 fromX = fromY = -1;
\r
5450 case EP_BlackKnight:
\r
5451 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5452 fromX = fromY = -1;
\r
5455 case EP_BlackBishop:
\r
5456 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5457 fromX = fromY = -1;
\r
5460 case EP_BlackRook:
\r
5461 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5462 fromX = fromY = -1;
\r
5465 case EP_BlackQueen:
\r
5466 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5467 fromX = fromY = -1;
\r
5470 case EP_BlackFerz:
\r
5471 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5472 fromX = fromY = -1;
\r
5475 case EP_BlackWazir:
\r
5476 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5477 fromX = fromY = -1;
\r
5480 case EP_BlackAlfil:
\r
5481 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5482 fromX = fromY = -1;
\r
5485 case EP_BlackCannon:
\r
5486 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5487 fromX = fromY = -1;
\r
5490 case EP_BlackCardinal:
\r
5491 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5492 fromX = fromY = -1;
\r
5495 case EP_BlackMarshall:
\r
5496 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5497 fromX = fromY = -1;
\r
5500 case EP_BlackKing:
\r
5501 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5502 fromX = fromY = -1;
\r
5505 case EP_EmptySquare:
\r
5506 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5507 fromX = fromY = -1;
\r
5510 case EP_ClearBoard:
\r
5511 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5512 fromX = fromY = -1;
\r
5516 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5517 fromX = fromY = -1;
\r
5521 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5522 fromX = fromY = -1;
\r
5526 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5527 fromX = fromY = -1;
\r
5531 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5532 fromX = fromY = -1;
\r
5536 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5537 fromX = fromY = -1;
\r
5541 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5542 fromX = fromY = -1;
\r
5546 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5547 fromX = fromY = -1;
\r
5551 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5552 fromX = fromY = -1;
\r
5556 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5557 fromX = fromY = -1;
\r
5561 barbaric = 0; appData.language = "";
\r
5562 TranslateMenus(0);
\r
5563 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5564 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5565 lastChecked = wmId;
\r
5569 if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)
\r
5570 RecentEngineEvent(wmId - IDM_RecentEngines);
\r
5572 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5573 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5574 TranslateMenus(0);
\r
5575 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5576 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5577 lastChecked = wmId;
\r
5580 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5586 case CLOCK_TIMER_ID:
\r
5587 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5588 clockTimerEvent = 0;
\r
5589 DecrementClocks(); /* call into back end */
\r
5591 case LOAD_GAME_TIMER_ID:
\r
5592 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5593 loadGameTimerEvent = 0;
\r
5594 AutoPlayGameLoop(); /* call into back end */
\r
5596 case ANALYSIS_TIMER_ID:
\r
5597 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5598 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5599 AnalysisPeriodicEvent(0);
\r
5601 KillTimer(hwnd, analysisTimerEvent);
\r
5602 analysisTimerEvent = 0;
\r
5605 case DELAYED_TIMER_ID:
\r
5606 KillTimer(hwnd, delayedTimerEvent);
\r
5607 delayedTimerEvent = 0;
\r
5608 delayedTimerCallback();
\r
5613 case WM_USER_Input:
\r
5614 InputEvent(hwnd, message, wParam, lParam);
\r
5617 /* [AS] Also move "attached" child windows */
\r
5618 case WM_WINDOWPOSCHANGING:
\r
5620 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5621 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5623 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5624 /* Window is moving */
\r
5627 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5628 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5629 rcMain.right = wpMain.x + wpMain.width;
\r
5630 rcMain.top = wpMain.y;
\r
5631 rcMain.bottom = wpMain.y + wpMain.height;
\r
5633 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5634 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5635 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5636 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5637 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5638 wpMain.x = lpwp->x;
\r
5639 wpMain.y = lpwp->y;
\r
5644 /* [AS] Snapping */
\r
5645 case WM_ENTERSIZEMOVE:
\r
5646 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5647 if (hwnd == hwndMain) {
\r
5648 doingSizing = TRUE;
\r
5651 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5655 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5656 if (hwnd == hwndMain) {
\r
5657 lastSizing = wParam;
\r
5662 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5663 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5665 case WM_EXITSIZEMOVE:
\r
5666 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5667 if (hwnd == hwndMain) {
\r
5669 doingSizing = FALSE;
\r
5670 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5671 GetClientRect(hwnd, &client);
\r
5672 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5674 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5676 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5679 case WM_DESTROY: /* message: window being destroyed */
\r
5680 PostQuitMessage(0);
\r
5684 if (hwnd == hwndMain) {
\r
5689 default: /* Passes it on if unprocessed */
\r
5690 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5695 /*---------------------------------------------------------------------------*\
\r
5697 * Misc utility routines
\r
5699 \*---------------------------------------------------------------------------*/
\r
5702 * Decent random number generator, at least not as bad as Windows
\r
5703 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5705 unsigned int randstate;
\r
5710 randstate = randstate * 1664525 + 1013904223;
\r
5711 return (int) randstate & 0x7fffffff;
\r
5715 mysrandom(unsigned int seed)
\r
5722 * returns TRUE if user selects a different color, FALSE otherwise
\r
5726 ChangeColor(HWND hwnd, COLORREF *which)
\r
5728 static BOOL firstTime = TRUE;
\r
5729 static DWORD customColors[16];
\r
5731 COLORREF newcolor;
\r
5736 /* Make initial colors in use available as custom colors */
\r
5737 /* Should we put the compiled-in defaults here instead? */
\r
5739 customColors[i++] = lightSquareColor & 0xffffff;
\r
5740 customColors[i++] = darkSquareColor & 0xffffff;
\r
5741 customColors[i++] = whitePieceColor & 0xffffff;
\r
5742 customColors[i++] = blackPieceColor & 0xffffff;
\r
5743 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5744 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5746 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5747 customColors[i++] = textAttribs[ccl].color;
\r
5749 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5750 firstTime = FALSE;
\r
5753 cc.lStructSize = sizeof(cc);
\r
5754 cc.hwndOwner = hwnd;
\r
5755 cc.hInstance = NULL;
\r
5756 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5757 cc.lpCustColors = (LPDWORD) customColors;
\r
5758 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5760 if (!ChooseColor(&cc)) return FALSE;
\r
5762 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5763 if (newcolor == *which) return FALSE;
\r
5764 *which = newcolor;
\r
5768 InitDrawingColors();
\r
5769 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5774 MyLoadSound(MySound *ms)
\r
5780 if (ms->data && ms->flag) free(ms->data);
\r
5783 switch (ms->name[0]) {
\r
5789 /* System sound from Control Panel. Don't preload here. */
\r
5793 if (ms->name[1] == NULLCHAR) {
\r
5794 /* "!" alone = silence */
\r
5797 /* Builtin wave resource. Error if not found. */
\r
5798 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5799 if (h == NULL) break;
\r
5800 ms->data = (void *)LoadResource(hInst, h);
\r
5801 ms->flag = 0; // not maloced, so cannot be freed!
\r
5802 if (h == NULL) break;
\r
5807 /* .wav file. Error if not found. */
\r
5808 f = fopen(ms->name, "rb");
\r
5809 if (f == NULL) break;
\r
5810 if (fstat(fileno(f), &st) < 0) break;
\r
5811 ms->data = malloc(st.st_size);
\r
5813 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5819 char buf[MSG_SIZ];
\r
5820 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5821 DisplayError(buf, GetLastError());
\r
5827 MyPlaySound(MySound *ms)
\r
5829 BOOLEAN ok = FALSE;
\r
5831 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5832 switch (ms->name[0]) {
\r
5834 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5839 /* System sound from Control Panel (deprecated feature).
\r
5840 "$" alone or an unset sound name gets default beep (still in use). */
\r
5841 if (ms->name[1]) {
\r
5842 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5844 if (!ok) ok = MessageBeep(MB_OK);
\r
5847 /* Builtin wave resource, or "!" alone for silence */
\r
5848 if (ms->name[1]) {
\r
5849 if (ms->data == NULL) return FALSE;
\r
5850 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5856 /* .wav file. Error if not found. */
\r
5857 if (ms->data == NULL) return FALSE;
\r
5858 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5861 /* Don't print an error: this can happen innocently if the sound driver
\r
5862 is busy; for instance, if another instance of WinBoard is playing
\r
5863 a sound at about the same time. */
\r
5869 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5872 OPENFILENAME *ofn;
\r
5873 static UINT *number; /* gross that this is static */
\r
5875 switch (message) {
\r
5876 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5877 /* Center the dialog over the application window */
\r
5878 ofn = (OPENFILENAME *) lParam;
\r
5879 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5880 number = (UINT *) ofn->lCustData;
\r
5881 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5885 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5886 Translate(hDlg, 1536);
\r
5887 return FALSE; /* Allow for further processing */
\r
5890 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5891 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5893 return FALSE; /* Allow for further processing */
\r
5899 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5901 static UINT *number;
\r
5902 OPENFILENAME *ofname;
\r
5905 case WM_INITDIALOG:
\r
5906 Translate(hdlg, DLG_IndexNumber);
\r
5907 ofname = (OPENFILENAME *)lParam;
\r
5908 number = (UINT *)(ofname->lCustData);
\r
5911 ofnot = (OFNOTIFY *)lParam;
\r
5912 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5913 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5922 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5923 char *nameFilt, char *dlgTitle, UINT *number,
\r
5924 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5926 OPENFILENAME openFileName;
\r
5927 char buf1[MSG_SIZ];
\r
5930 if (fileName == NULL) fileName = buf1;
\r
5931 if (defName == NULL) {
\r
5932 safeStrCpy(fileName, "*.", 3 );
\r
5933 strcat(fileName, defExt);
\r
5935 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5937 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5938 if (number) *number = 0;
\r
5940 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5941 openFileName.hwndOwner = hwnd;
\r
5942 openFileName.hInstance = (HANDLE) hInst;
\r
5943 openFileName.lpstrFilter = nameFilt;
\r
5944 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5945 openFileName.nMaxCustFilter = 0L;
\r
5946 openFileName.nFilterIndex = 1L;
\r
5947 openFileName.lpstrFile = fileName;
\r
5948 openFileName.nMaxFile = MSG_SIZ;
\r
5949 openFileName.lpstrFileTitle = fileTitle;
\r
5950 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5951 openFileName.lpstrInitialDir = NULL;
\r
5952 openFileName.lpstrTitle = dlgTitle;
\r
5953 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5954 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5955 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5956 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5957 openFileName.nFileOffset = 0;
\r
5958 openFileName.nFileExtension = 0;
\r
5959 openFileName.lpstrDefExt = defExt;
\r
5960 openFileName.lCustData = (LONG) number;
\r
5961 openFileName.lpfnHook = oldDialog ?
\r
5962 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5963 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5965 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5966 GetOpenFileName(&openFileName)) {
\r
5967 /* open the file */
\r
5968 f = fopen(openFileName.lpstrFile, write);
\r
5970 MessageBox(hwnd, _("File open failed"), NULL,
\r
5971 MB_OK|MB_ICONEXCLAMATION);
\r
5975 int err = CommDlgExtendedError();
\r
5976 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5985 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5987 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5990 * Get the first pop-up menu in the menu template. This is the
\r
5991 * menu that TrackPopupMenu displays.
\r
5993 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5994 TranslateOneMenu(10, hmenuTrackPopup);
\r
5996 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5999 * TrackPopup uses screen coordinates, so convert the
\r
6000 * coordinates of the mouse click to screen coordinates.
\r
6002 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
6004 /* Draw and track the floating pop-up menu. */
\r
6005 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
6006 pt.x, pt.y, 0, hwnd, NULL);
\r
6008 /* Destroy the menu.*/
\r
6009 DestroyMenu(hmenu);
\r
6014 int sizeX, sizeY, newSizeX, newSizeY;
\r
6016 } ResizeEditPlusButtonsClosure;
\r
6019 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
6021 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
6025 if (hChild == cl->hText) return TRUE;
\r
6026 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
6027 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
6028 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
6029 ScreenToClient(cl->hDlg, &pt);
\r
6030 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
6031 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
6035 /* Resize a dialog that has a (rich) edit field filling most of
\r
6036 the top, with a row of buttons below */
\r
6038 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
6041 int newTextHeight, newTextWidth;
\r
6042 ResizeEditPlusButtonsClosure cl;
\r
6044 /*if (IsIconic(hDlg)) return;*/
\r
6045 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
6047 cl.hdwp = BeginDeferWindowPos(8);
\r
6049 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
6050 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6051 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6052 if (newTextHeight < 0) {
\r
6053 newSizeY += -newTextHeight;
\r
6054 newTextHeight = 0;
\r
6056 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
6057 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6063 cl.newSizeX = newSizeX;
\r
6064 cl.newSizeY = newSizeY;
\r
6065 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
6067 EndDeferWindowPos(cl.hdwp);
\r
6070 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
6072 RECT rChild, rParent;
\r
6073 int wChild, hChild, wParent, hParent;
\r
6074 int wScreen, hScreen, xNew, yNew;
\r
6077 /* Get the Height and Width of the child window */
\r
6078 GetWindowRect (hwndChild, &rChild);
\r
6079 wChild = rChild.right - rChild.left;
\r
6080 hChild = rChild.bottom - rChild.top;
\r
6082 /* Get the Height and Width of the parent window */
\r
6083 GetWindowRect (hwndParent, &rParent);
\r
6084 wParent = rParent.right - rParent.left;
\r
6085 hParent = rParent.bottom - rParent.top;
\r
6087 /* Get the display limits */
\r
6088 hdc = GetDC (hwndChild);
\r
6089 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
6090 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
6091 ReleaseDC(hwndChild, hdc);
\r
6093 /* Calculate new X position, then adjust for screen */
\r
6094 xNew = rParent.left + ((wParent - wChild) /2);
\r
6097 } else if ((xNew+wChild) > wScreen) {
\r
6098 xNew = wScreen - wChild;
\r
6101 /* Calculate new Y position, then adjust for screen */
\r
6103 yNew = rParent.top + ((hParent - hChild) /2);
\r
6106 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
6111 } else if ((yNew+hChild) > hScreen) {
\r
6112 yNew = hScreen - hChild;
\r
6115 /* Set it, and return */
\r
6116 return SetWindowPos (hwndChild, NULL,
\r
6117 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
6120 /* Center one window over another */
\r
6121 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
6123 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
6126 /*---------------------------------------------------------------------------*\
\r
6128 * Startup Dialog functions
\r
6130 \*---------------------------------------------------------------------------*/
\r
6132 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
6134 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6136 while (*cd != NULL) {
\r
6137 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
6143 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
6145 char buf1[MAX_ARG_LEN];
\r
6148 if (str[0] == '@') {
\r
6149 FILE* f = fopen(str + 1, "r");
\r
6151 DisplayFatalError(str + 1, errno, 2);
\r
6154 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
6156 buf1[len] = NULLCHAR;
\r
6160 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6163 char buf[MSG_SIZ];
\r
6164 char *end = strchr(str, '\n');
\r
6165 if (end == NULL) return;
\r
6166 memcpy(buf, str, end - str);
\r
6167 buf[end - str] = NULLCHAR;
\r
6168 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6174 SetStartupDialogEnables(HWND hDlg)
\r
6176 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6177 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6178 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6179 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6180 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6181 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6182 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6183 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6184 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6185 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6186 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6187 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6188 IsDlgButtonChecked(hDlg, OPT_View));
\r
6192 QuoteForFilename(char *filename)
\r
6194 int dquote, space;
\r
6195 dquote = strchr(filename, '"') != NULL;
\r
6196 space = strchr(filename, ' ') != NULL;
\r
6197 if (dquote || space) {
\r
6209 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6211 char buf[MSG_SIZ];
\r
6214 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6215 q = QuoteForFilename(nthcp);
\r
6216 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6217 if (*nthdir != NULLCHAR) {
\r
6218 q = QuoteForFilename(nthdir);
\r
6219 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6221 if (*nthcp == NULLCHAR) {
\r
6222 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6223 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6224 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6225 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6230 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6232 char buf[MSG_SIZ];
\r
6236 switch (message) {
\r
6237 case WM_INITDIALOG:
\r
6238 /* Center the dialog */
\r
6239 CenterWindow (hDlg, GetDesktopWindow());
\r
6240 Translate(hDlg, DLG_Startup);
\r
6241 /* Initialize the dialog items */
\r
6242 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6243 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6244 firstChessProgramNames);
\r
6245 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6246 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6247 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6248 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6249 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6250 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6251 if (*appData.icsHelper != NULLCHAR) {
\r
6252 char *q = QuoteForFilename(appData.icsHelper);
\r
6253 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6255 if (*appData.icsHost == NULLCHAR) {
\r
6256 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6257 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6258 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6259 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6260 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6263 if (appData.icsActive) {
\r
6264 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6266 else if (appData.noChessProgram) {
\r
6267 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6270 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6273 SetStartupDialogEnables(hDlg);
\r
6277 switch (LOWORD(wParam)) {
\r
6279 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6280 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6281 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6283 comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox
\r
6284 ParseArgs(StringGet, &p);
\r
6285 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6286 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6288 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6289 ParseArgs(StringGet, &p);
\r
6290 SwapEngines(singleList); // ... and then make it 'second'
\r
6291 appData.noChessProgram = FALSE;
\r
6292 appData.icsActive = FALSE;
\r
6293 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6294 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6295 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6297 ParseArgs(StringGet, &p);
\r
6298 if (appData.zippyPlay) {
\r
6299 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6300 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6302 ParseArgs(StringGet, &p);
\r
6304 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6305 appData.noChessProgram = TRUE;
\r
6306 appData.icsActive = FALSE;
\r
6308 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6309 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6312 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6313 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6315 ParseArgs(StringGet, &p);
\r
6317 EndDialog(hDlg, TRUE);
\r
6324 case IDM_HELPCONTENTS:
\r
6325 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6326 MessageBox (GetFocus(),
\r
6327 _("Unable to activate help"),
\r
6328 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6333 SetStartupDialogEnables(hDlg);
\r
6341 /*---------------------------------------------------------------------------*\
\r
6343 * About box dialog functions
\r
6345 \*---------------------------------------------------------------------------*/
\r
6347 /* Process messages for "About" dialog box */
\r
6349 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6351 switch (message) {
\r
6352 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6353 /* Center the dialog over the application window */
\r
6354 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6355 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6356 Translate(hDlg, ABOUTBOX);
\r
6360 case WM_COMMAND: /* message: received a command */
\r
6361 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6362 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6363 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6371 /*---------------------------------------------------------------------------*\
\r
6373 * Comment Dialog functions
\r
6375 \*---------------------------------------------------------------------------*/
\r
6378 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6380 static HANDLE hwndText = NULL;
\r
6381 int len, newSizeX, newSizeY, flags;
\r
6382 static int sizeX, sizeY;
\r
6387 switch (message) {
\r
6388 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6389 /* Initialize the dialog items */
\r
6390 Translate(hDlg, DLG_EditComment);
\r
6391 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6392 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6393 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6394 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6395 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6396 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6397 SetWindowText(hDlg, commentTitle);
\r
6398 if (editComment) {
\r
6399 SetFocus(hwndText);
\r
6401 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6403 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6404 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6405 MAKELPARAM(FALSE, 0));
\r
6406 /* Size and position the dialog */
\r
6407 if (!commentDialog) {
\r
6408 commentDialog = hDlg;
\r
6409 flags = SWP_NOZORDER;
\r
6410 GetClientRect(hDlg, &rect);
\r
6411 sizeX = rect.right;
\r
6412 sizeY = rect.bottom;
\r
6413 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6414 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6415 WINDOWPLACEMENT wp;
\r
6416 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6417 wp.length = sizeof(WINDOWPLACEMENT);
\r
6419 wp.showCmd = SW_SHOW;
\r
6420 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6421 wp.rcNormalPosition.left = wpComment.x;
\r
6422 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6423 wp.rcNormalPosition.top = wpComment.y;
\r
6424 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6425 SetWindowPlacement(hDlg, &wp);
\r
6427 GetClientRect(hDlg, &rect);
\r
6428 newSizeX = rect.right;
\r
6429 newSizeY = rect.bottom;
\r
6430 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6431 newSizeX, newSizeY);
\r
6436 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6439 case WM_COMMAND: /* message: received a command */
\r
6440 switch (LOWORD(wParam)) {
\r
6442 if (editComment) {
\r
6444 /* Read changed options from the dialog box */
\r
6445 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6446 len = GetWindowTextLength(hwndText);
\r
6447 str = (char *) malloc(len + 1);
\r
6448 GetWindowText(hwndText, str, len + 1);
\r
6457 ReplaceComment(commentIndex, str);
\r
6464 case OPT_CancelComment:
\r
6468 case OPT_ClearComment:
\r
6469 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6472 case OPT_EditComment:
\r
6473 EditCommentEvent();
\r
6481 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6482 if( wParam == OPT_CommentText ) {
\r
6483 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6485 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6486 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6490 pt.x = LOWORD( lpMF->lParam );
\r
6491 pt.y = HIWORD( lpMF->lParam );
\r
6493 if(lpMF->msg == WM_CHAR) {
\r
6495 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6496 index = sel.cpMin;
\r
6498 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6500 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6501 len = GetWindowTextLength(hwndText);
\r
6502 str = (char *) malloc(len + 1);
\r
6503 GetWindowText(hwndText, str, len + 1);
\r
6504 ReplaceComment(commentIndex, str);
\r
6505 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6506 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6509 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6510 lpMF->msg = WM_USER;
\r
6518 newSizeX = LOWORD(lParam);
\r
6519 newSizeY = HIWORD(lParam);
\r
6520 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6525 case WM_GETMINMAXINFO:
\r
6526 /* Prevent resizing window too small */
\r
6527 mmi = (MINMAXINFO *) lParam;
\r
6528 mmi->ptMinTrackSize.x = 100;
\r
6529 mmi->ptMinTrackSize.y = 100;
\r
6536 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6541 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6543 if (str == NULL) str = "";
\r
6544 p = (char *) malloc(2 * strlen(str) + 2);
\r
6547 if (*str == '\n') *q++ = '\r';
\r
6551 if (commentText != NULL) free(commentText);
\r
6553 commentIndex = index;
\r
6554 commentTitle = title;
\r
6556 editComment = edit;
\r
6558 if (commentDialog) {
\r
6559 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6560 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6562 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6563 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6564 hwndMain, (DLGPROC)lpProc);
\r
6565 FreeProcInstance(lpProc);
\r
6571 /*---------------------------------------------------------------------------*\
\r
6573 * Type-in move dialog functions
\r
6575 \*---------------------------------------------------------------------------*/
\r
6578 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6580 char move[MSG_SIZ];
\r
6583 switch (message) {
\r
6584 case WM_INITDIALOG:
\r
6585 move[0] = (char) lParam;
\r
6586 move[1] = NULLCHAR;
\r
6587 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6588 Translate(hDlg, DLG_TypeInMove);
\r
6589 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6590 SetWindowText(hInput, move);
\r
6592 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6596 switch (LOWORD(wParam)) {
\r
6599 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6600 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6601 TypeInDoneEvent(move);
\r
6602 EndDialog(hDlg, TRUE);
\r
6605 EndDialog(hDlg, FALSE);
\r
6616 PopUpMoveDialog(char firstchar)
\r
6620 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6621 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6622 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6623 FreeProcInstance(lpProc);
\r
6626 /*---------------------------------------------------------------------------*\
\r
6628 * Type-in name dialog functions
\r
6630 \*---------------------------------------------------------------------------*/
\r
6633 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6635 char move[MSG_SIZ];
\r
6638 switch (message) {
\r
6639 case WM_INITDIALOG:
\r
6640 move[0] = (char) lParam;
\r
6641 move[1] = NULLCHAR;
\r
6642 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6643 Translate(hDlg, DLG_TypeInName);
\r
6644 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6645 SetWindowText(hInput, move);
\r
6647 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6651 switch (LOWORD(wParam)) {
\r
6653 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6654 appData.userName = strdup(move);
\r
6657 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6658 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6659 DisplayTitle(move);
\r
6663 EndDialog(hDlg, TRUE);
\r
6666 EndDialog(hDlg, FALSE);
\r
6677 PopUpNameDialog(char firstchar)
\r
6681 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6682 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6683 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6684 FreeProcInstance(lpProc);
\r
6687 /*---------------------------------------------------------------------------*\
\r
6691 \*---------------------------------------------------------------------------*/
\r
6693 /* Nonmodal error box */
\r
6694 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6695 WPARAM wParam, LPARAM lParam);
\r
6698 ErrorPopUp(char *title, char *content)
\r
6702 BOOLEAN modal = hwndMain == NULL;
\r
6720 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6721 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6724 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6726 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6727 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6728 hwndMain, (DLGPROC)lpProc);
\r
6729 FreeProcInstance(lpProc);
\r
6736 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6737 if (errorDialog == NULL) return;
\r
6738 DestroyWindow(errorDialog);
\r
6739 errorDialog = NULL;
\r
6740 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6744 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6749 switch (message) {
\r
6750 case WM_INITDIALOG:
\r
6751 GetWindowRect(hDlg, &rChild);
\r
6754 SetWindowPos(hDlg, NULL, rChild.left,
\r
6755 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6756 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6760 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6761 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6762 and it doesn't work when you resize the dialog.
\r
6763 For now, just give it a default position.
\r
6765 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6766 Translate(hDlg, DLG_Error);
\r
6768 errorDialog = hDlg;
\r
6769 SetWindowText(hDlg, errorTitle);
\r
6770 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6771 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6775 switch (LOWORD(wParam)) {
\r
6778 if (errorDialog == hDlg) errorDialog = NULL;
\r
6779 DestroyWindow(hDlg);
\r
6791 HWND gothicDialog = NULL;
\r
6794 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6798 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6800 switch (message) {
\r
6801 case WM_INITDIALOG:
\r
6802 GetWindowRect(hDlg, &rChild);
\r
6804 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6808 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6809 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6810 and it doesn't work when you resize the dialog.
\r
6811 For now, just give it a default position.
\r
6813 gothicDialog = hDlg;
\r
6814 SetWindowText(hDlg, errorTitle);
\r
6815 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6816 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6820 switch (LOWORD(wParam)) {
\r
6823 if (errorDialog == hDlg) errorDialog = NULL;
\r
6824 DestroyWindow(hDlg);
\r
6836 GothicPopUp(char *title, VariantClass variant)
\r
6839 static char *lastTitle;
\r
6841 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6842 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6844 if(lastTitle != title && gothicDialog != NULL) {
\r
6845 DestroyWindow(gothicDialog);
\r
6846 gothicDialog = NULL;
\r
6848 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6849 title = lastTitle;
\r
6850 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6851 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6852 hwndMain, (DLGPROC)lpProc);
\r
6853 FreeProcInstance(lpProc);
\r
6858 /*---------------------------------------------------------------------------*\
\r
6860 * Ics Interaction console functions
\r
6862 \*---------------------------------------------------------------------------*/
\r
6864 #define HISTORY_SIZE 64
\r
6865 static char *history[HISTORY_SIZE];
\r
6866 int histIn = 0, histP = 0;
\r
6869 SaveInHistory(char *cmd)
\r
6871 if (history[histIn] != NULL) {
\r
6872 free(history[histIn]);
\r
6873 history[histIn] = NULL;
\r
6875 if (*cmd == NULLCHAR) return;
\r
6876 history[histIn] = StrSave(cmd);
\r
6877 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6878 if (history[histIn] != NULL) {
\r
6879 free(history[histIn]);
\r
6880 history[histIn] = NULL;
\r
6886 PrevInHistory(char *cmd)
\r
6889 if (histP == histIn) {
\r
6890 if (history[histIn] != NULL) free(history[histIn]);
\r
6891 history[histIn] = StrSave(cmd);
\r
6893 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6894 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6896 return history[histP];
\r
6902 if (histP == histIn) return NULL;
\r
6903 histP = (histP + 1) % HISTORY_SIZE;
\r
6904 return history[histP];
\r
6908 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6912 hmenu = LoadMenu(hInst, "TextMenu");
\r
6913 h = GetSubMenu(hmenu, 0);
\r
6915 if (strcmp(e->item, "-") == 0) {
\r
6916 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6917 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6918 int flags = MF_STRING, j = 0;
\r
6919 if (e->item[0] == '|') {
\r
6920 flags |= MF_MENUBARBREAK;
\r
6923 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6924 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6932 WNDPROC consoleTextWindowProc;
\r
6935 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6937 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6938 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6942 SetWindowText(hInput, command);
\r
6944 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6946 sel.cpMin = 999999;
\r
6947 sel.cpMax = 999999;
\r
6948 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6953 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6954 if (sel.cpMin == sel.cpMax) {
\r
6955 /* Expand to surrounding word */
\r
6958 tr.chrg.cpMax = sel.cpMin;
\r
6959 tr.chrg.cpMin = --sel.cpMin;
\r
6960 if (sel.cpMin < 0) break;
\r
6961 tr.lpstrText = name;
\r
6962 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6963 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6967 tr.chrg.cpMin = sel.cpMax;
\r
6968 tr.chrg.cpMax = ++sel.cpMax;
\r
6969 tr.lpstrText = name;
\r
6970 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6971 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6974 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6975 MessageBeep(MB_ICONEXCLAMATION);
\r
6979 tr.lpstrText = name;
\r
6980 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6982 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6983 MessageBeep(MB_ICONEXCLAMATION);
\r
6986 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6989 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
6990 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6991 SetWindowText(hInput, buf);
\r
6992 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6994 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6995 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6996 SetWindowText(hInput, buf);
\r
6997 sel.cpMin = 999999;
\r
6998 sel.cpMax = 999999;
\r
6999 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7005 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7010 switch (message) {
\r
7012 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7013 if(wParam=='R') return 0;
\r
7016 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
7019 sel.cpMin = 999999;
\r
7020 sel.cpMax = 999999;
\r
7021 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7022 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
7027 if(wParam != '\022') {
\r
7028 if (wParam == '\t') {
\r
7029 if (GetKeyState(VK_SHIFT) < 0) {
\r
7031 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7032 if (buttonDesc[0].hwnd) {
\r
7033 SetFocus(buttonDesc[0].hwnd);
\r
7035 SetFocus(hwndMain);
\r
7039 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
7042 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7043 JAWS_DELETE( SetFocus(hInput); )
\r
7044 SendMessage(hInput, message, wParam, lParam);
\r
7047 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
7049 case WM_RBUTTONDOWN:
\r
7050 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
7051 /* Move selection here if it was empty */
\r
7053 pt.x = LOWORD(lParam);
\r
7054 pt.y = HIWORD(lParam);
\r
7055 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7056 if (sel.cpMin == sel.cpMax) {
\r
7057 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
7058 sel.cpMax = sel.cpMin;
\r
7059 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7061 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
7062 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
7064 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
7065 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7066 if (sel.cpMin == sel.cpMax) {
\r
7067 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7068 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
7070 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7071 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7073 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
7074 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
7075 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
7076 MenuPopup(hwnd, pt, hmenu, -1);
\r
7080 case WM_RBUTTONUP:
\r
7081 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7082 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7083 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7087 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7089 return SendMessage(hInput, message, wParam, lParam);
\r
7090 case WM_MBUTTONDOWN:
\r
7091 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7093 switch (LOWORD(wParam)) {
\r
7094 case IDM_QuickPaste:
\r
7096 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7097 if (sel.cpMin == sel.cpMax) {
\r
7098 MessageBeep(MB_ICONEXCLAMATION);
\r
7101 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7102 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7103 SendMessage(hInput, WM_PASTE, 0, 0);
\r
7108 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7111 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7114 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7118 int i = LOWORD(wParam) - IDM_CommandX;
\r
7119 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
7120 icsTextMenuEntry[i].command != NULL) {
\r
7121 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
7122 icsTextMenuEntry[i].getname,
\r
7123 icsTextMenuEntry[i].immediate);
\r
7131 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
7134 WNDPROC consoleInputWindowProc;
\r
7137 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7139 char buf[MSG_SIZ];
\r
7141 static BOOL sendNextChar = FALSE;
\r
7142 static BOOL quoteNextChar = FALSE;
\r
7143 InputSource *is = consoleInputSource;
\r
7147 switch (message) {
\r
7149 if (!appData.localLineEditing || sendNextChar) {
\r
7150 is->buf[0] = (CHAR) wParam;
\r
7152 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7153 sendNextChar = FALSE;
\r
7156 if (quoteNextChar) {
\r
7157 buf[0] = (char) wParam;
\r
7158 buf[1] = NULLCHAR;
\r
7159 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7160 quoteNextChar = FALSE;
\r
7164 case '\r': /* Enter key */
\r
7165 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7166 if (consoleEcho) SaveInHistory(is->buf);
\r
7167 is->buf[is->count++] = '\n';
\r
7168 is->buf[is->count] = NULLCHAR;
\r
7169 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7170 if (consoleEcho) {
\r
7171 ConsoleOutput(is->buf, is->count, TRUE);
\r
7172 } else if (appData.localLineEditing) {
\r
7173 ConsoleOutput("\n", 1, TRUE);
\r
7176 case '\033': /* Escape key */
\r
7177 SetWindowText(hwnd, "");
\r
7178 cf.cbSize = sizeof(CHARFORMAT);
\r
7179 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7180 if (consoleEcho) {
\r
7181 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7183 cf.crTextColor = COLOR_ECHOOFF;
\r
7185 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7186 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7188 case '\t': /* Tab key */
\r
7189 if (GetKeyState(VK_SHIFT) < 0) {
\r
7191 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7194 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7195 if (buttonDesc[0].hwnd) {
\r
7196 SetFocus(buttonDesc[0].hwnd);
\r
7198 SetFocus(hwndMain);
\r
7202 case '\023': /* Ctrl+S */
\r
7203 sendNextChar = TRUE;
\r
7205 case '\021': /* Ctrl+Q */
\r
7206 quoteNextChar = TRUE;
\r
7216 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7217 p = PrevInHistory(buf);
\r
7219 SetWindowText(hwnd, p);
\r
7220 sel.cpMin = 999999;
\r
7221 sel.cpMax = 999999;
\r
7222 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7227 p = NextInHistory();
\r
7229 SetWindowText(hwnd, p);
\r
7230 sel.cpMin = 999999;
\r
7231 sel.cpMax = 999999;
\r
7232 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7238 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7242 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7246 case WM_MBUTTONDOWN:
\r
7247 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7248 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7250 case WM_RBUTTONUP:
\r
7251 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7252 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7253 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7257 hmenu = LoadMenu(hInst, "InputMenu");
\r
7258 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7259 if (sel.cpMin == sel.cpMax) {
\r
7260 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7261 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7263 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7264 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7266 pt.x = LOWORD(lParam);
\r
7267 pt.y = HIWORD(lParam);
\r
7268 MenuPopup(hwnd, pt, hmenu, -1);
\r
7272 switch (LOWORD(wParam)) {
\r
7274 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7276 case IDM_SelectAll:
\r
7278 sel.cpMax = -1; /*999999?*/
\r
7279 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7282 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7285 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7288 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7293 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7296 #define CO_MAX 100000
\r
7297 #define CO_TRIM 1000
\r
7300 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7302 static SnapData sd;
\r
7303 HWND hText, hInput;
\r
7305 static int sizeX, sizeY;
\r
7306 int newSizeX, newSizeY;
\r
7310 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7311 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7313 switch (message) {
\r
7315 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7317 ENLINK *pLink = (ENLINK*)lParam;
\r
7318 if (pLink->msg == WM_LBUTTONUP)
\r
7322 tr.chrg = pLink->chrg;
\r
7323 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7324 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7325 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7326 free(tr.lpstrText);
\r
7330 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7331 hwndConsole = hDlg;
\r
7333 consoleTextWindowProc = (WNDPROC)
\r
7334 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7335 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7336 consoleInputWindowProc = (WNDPROC)
\r
7337 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7338 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7339 Colorize(ColorNormal, TRUE);
\r
7340 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7341 ChangedConsoleFont();
\r
7342 GetClientRect(hDlg, &rect);
\r
7343 sizeX = rect.right;
\r
7344 sizeY = rect.bottom;
\r
7345 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7346 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7347 WINDOWPLACEMENT wp;
\r
7348 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7349 wp.length = sizeof(WINDOWPLACEMENT);
\r
7351 wp.showCmd = SW_SHOW;
\r
7352 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7353 wp.rcNormalPosition.left = wpConsole.x;
\r
7354 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7355 wp.rcNormalPosition.top = wpConsole.y;
\r
7356 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7357 SetWindowPlacement(hDlg, &wp);
\r
7360 // [HGM] Chessknight's change 2004-07-13
\r
7361 else { /* Determine Defaults */
\r
7362 WINDOWPLACEMENT wp;
\r
7363 wpConsole.x = wpMain.width + 1;
\r
7364 wpConsole.y = wpMain.y;
\r
7365 wpConsole.width = screenWidth - wpMain.width;
\r
7366 wpConsole.height = wpMain.height;
\r
7367 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7368 wp.length = sizeof(WINDOWPLACEMENT);
\r
7370 wp.showCmd = SW_SHOW;
\r
7371 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7372 wp.rcNormalPosition.left = wpConsole.x;
\r
7373 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7374 wp.rcNormalPosition.top = wpConsole.y;
\r
7375 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7376 SetWindowPlacement(hDlg, &wp);
\r
7379 // Allow hText to highlight URLs and send notifications on them
\r
7380 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7381 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7382 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7383 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7397 if (IsIconic(hDlg)) break;
\r
7398 newSizeX = LOWORD(lParam);
\r
7399 newSizeY = HIWORD(lParam);
\r
7400 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7401 RECT rectText, rectInput;
\r
7403 int newTextHeight, newTextWidth;
\r
7404 GetWindowRect(hText, &rectText);
\r
7405 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7406 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7407 if (newTextHeight < 0) {
\r
7408 newSizeY += -newTextHeight;
\r
7409 newTextHeight = 0;
\r
7411 SetWindowPos(hText, NULL, 0, 0,
\r
7412 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7413 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7414 pt.x = rectInput.left;
\r
7415 pt.y = rectInput.top + newSizeY - sizeY;
\r
7416 ScreenToClient(hDlg, &pt);
\r
7417 SetWindowPos(hInput, NULL,
\r
7418 pt.x, pt.y, /* needs client coords */
\r
7419 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7420 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7426 case WM_GETMINMAXINFO:
\r
7427 /* Prevent resizing window too small */
\r
7428 mmi = (MINMAXINFO *) lParam;
\r
7429 mmi->ptMinTrackSize.x = 100;
\r
7430 mmi->ptMinTrackSize.y = 100;
\r
7433 /* [AS] Snapping */
\r
7434 case WM_ENTERSIZEMOVE:
\r
7435 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7438 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7441 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7443 case WM_EXITSIZEMOVE:
\r
7444 UpdateICSWidth(hText);
\r
7445 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7448 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7456 if (hwndConsole) return;
\r
7457 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7458 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7463 ConsoleOutput(char* data, int length, int forceVisible)
\r
7468 char buf[CO_MAX+1];
\r
7471 static int delayLF = 0;
\r
7472 CHARRANGE savesel, sel;
\r
7474 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7482 while (length--) {
\r
7490 } else if (*p == '\007') {
\r
7491 MyPlaySound(&sounds[(int)SoundBell]);
\r
7498 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7499 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7500 /* Save current selection */
\r
7501 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7502 exlen = GetWindowTextLength(hText);
\r
7503 /* Find out whether current end of text is visible */
\r
7504 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7505 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7506 /* Trim existing text if it's too long */
\r
7507 if (exlen + (q - buf) > CO_MAX) {
\r
7508 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7511 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7512 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7514 savesel.cpMin -= trim;
\r
7515 savesel.cpMax -= trim;
\r
7516 if (exlen < 0) exlen = 0;
\r
7517 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7518 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7520 /* Append the new text */
\r
7521 sel.cpMin = exlen;
\r
7522 sel.cpMax = exlen;
\r
7523 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7524 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7525 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7526 if (forceVisible || exlen == 0 ||
\r
7527 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7528 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7529 /* Scroll to make new end of text visible if old end of text
\r
7530 was visible or new text is an echo of user typein */
\r
7531 sel.cpMin = 9999999;
\r
7532 sel.cpMax = 9999999;
\r
7533 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7534 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7535 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7536 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7538 if (savesel.cpMax == exlen || forceVisible) {
\r
7539 /* Move insert point to new end of text if it was at the old
\r
7540 end of text or if the new text is an echo of user typein */
\r
7541 sel.cpMin = 9999999;
\r
7542 sel.cpMax = 9999999;
\r
7543 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7545 /* Restore previous selection */
\r
7546 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7548 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7555 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7559 COLORREF oldFg, oldBg;
\r
7563 if(copyNumber > 1)
\r
7564 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7566 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7567 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7568 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7571 rect.right = x + squareSize;
\r
7573 rect.bottom = y + squareSize;
\r
7576 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7577 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7578 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7579 &rect, str, strlen(str), NULL);
\r
7581 (void) SetTextColor(hdc, oldFg);
\r
7582 (void) SetBkColor(hdc, oldBg);
\r
7583 (void) SelectObject(hdc, oldFont);
\r
7587 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7588 RECT *rect, char *color, char *flagFell)
\r
7592 COLORREF oldFg, oldBg;
\r
7595 if (twoBoards && partnerUp) return;
\r
7596 if (appData.clockMode) {
\r
7598 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7600 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7607 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7608 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7610 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7611 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7613 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7617 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7618 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7619 rect, str, strlen(str), NULL);
\r
7620 if(logoHeight > 0 && appData.clockMode) {
\r
7622 str += strlen(color)+2;
\r
7623 r.top = rect->top + logoHeight/2;
\r
7624 r.left = rect->left;
\r
7625 r.right = rect->right;
\r
7626 r.bottom = rect->bottom;
\r
7627 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7628 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7629 &r, str, strlen(str), NULL);
\r
7631 (void) SetTextColor(hdc, oldFg);
\r
7632 (void) SetBkColor(hdc, oldBg);
\r
7633 (void) SelectObject(hdc, oldFont);
\r
7638 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7644 if( count <= 0 ) {
\r
7645 if (appData.debugMode) {
\r
7646 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7649 return ERROR_INVALID_USER_BUFFER;
\r
7652 ResetEvent(ovl->hEvent);
\r
7653 ovl->Offset = ovl->OffsetHigh = 0;
\r
7654 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7658 err = GetLastError();
\r
7659 if (err == ERROR_IO_PENDING) {
\r
7660 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7664 err = GetLastError();
\r
7671 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7676 ResetEvent(ovl->hEvent);
\r
7677 ovl->Offset = ovl->OffsetHigh = 0;
\r
7678 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7682 err = GetLastError();
\r
7683 if (err == ERROR_IO_PENDING) {
\r
7684 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7688 err = GetLastError();
\r
7694 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7695 void CheckForInputBufferFull( InputSource * is )
\r
7697 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7698 /* Look for end of line */
\r
7699 char * p = is->buf;
\r
7701 while( p < is->next && *p != '\n' ) {
\r
7705 if( p >= is->next ) {
\r
7706 if (appData.debugMode) {
\r
7707 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7710 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7711 is->count = (DWORD) -1;
\r
7712 is->next = is->buf;
\r
7718 InputThread(LPVOID arg)
\r
7723 is = (InputSource *) arg;
\r
7724 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7725 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7726 while (is->hThread != NULL) {
\r
7727 is->error = DoReadFile(is->hFile, is->next,
\r
7728 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7729 &is->count, &ovl);
\r
7730 if (is->error == NO_ERROR) {
\r
7731 is->next += is->count;
\r
7733 if (is->error == ERROR_BROKEN_PIPE) {
\r
7734 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7737 is->count = (DWORD) -1;
\r
7738 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7743 CheckForInputBufferFull( is );
\r
7745 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7747 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7749 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7752 CloseHandle(ovl.hEvent);
\r
7753 CloseHandle(is->hFile);
\r
7755 if (appData.debugMode) {
\r
7756 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7763 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7765 NonOvlInputThread(LPVOID arg)
\r
7772 is = (InputSource *) arg;
\r
7773 while (is->hThread != NULL) {
\r
7774 is->error = ReadFile(is->hFile, is->next,
\r
7775 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7776 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7777 if (is->error == NO_ERROR) {
\r
7778 /* Change CRLF to LF */
\r
7779 if (is->next > is->buf) {
\r
7781 i = is->count + 1;
\r
7789 if (prev == '\r' && *p == '\n') {
\r
7801 if (is->error == ERROR_BROKEN_PIPE) {
\r
7802 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7805 is->count = (DWORD) -1;
\r
7809 CheckForInputBufferFull( is );
\r
7811 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7813 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7815 if (is->count < 0) break; /* Quit on error */
\r
7817 CloseHandle(is->hFile);
\r
7822 SocketInputThread(LPVOID arg)
\r
7826 is = (InputSource *) arg;
\r
7827 while (is->hThread != NULL) {
\r
7828 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7829 if ((int)is->count == SOCKET_ERROR) {
\r
7830 is->count = (DWORD) -1;
\r
7831 is->error = WSAGetLastError();
\r
7833 is->error = NO_ERROR;
\r
7834 is->next += is->count;
\r
7835 if (is->count == 0 && is->second == is) {
\r
7836 /* End of file on stderr; quit with no message */
\r
7840 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7842 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7844 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7850 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7854 is = (InputSource *) lParam;
\r
7855 if (is->lineByLine) {
\r
7856 /* Feed in lines one by one */
\r
7857 char *p = is->buf;
\r
7859 while (q < is->next) {
\r
7860 if (*q++ == '\n') {
\r
7861 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7866 /* Move any partial line to the start of the buffer */
\r
7868 while (p < is->next) {
\r
7873 if (is->error != NO_ERROR || is->count == 0) {
\r
7874 /* Notify backend of the error. Note: If there was a partial
\r
7875 line at the end, it is not flushed through. */
\r
7876 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7879 /* Feed in the whole chunk of input at once */
\r
7880 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7881 is->next = is->buf;
\r
7885 /*---------------------------------------------------------------------------*\
\r
7887 * Menu enables. Used when setting various modes.
\r
7889 \*---------------------------------------------------------------------------*/
\r
7897 GreyRevert(Boolean grey)
\r
7898 { // [HGM] vari: for retracting variations in local mode
\r
7899 HMENU hmenu = GetMenu(hwndMain);
\r
7900 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7901 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7905 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7907 while (enab->item > 0) {
\r
7908 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7913 Enables gnuEnables[] = {
\r
7914 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7915 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7916 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7917 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7918 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7919 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7920 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7921 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7922 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7923 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7924 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7925 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7926 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7928 // Needed to switch from ncp to GNU mode on Engine Load
\r
7929 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7930 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7931 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7932 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7933 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7934 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7935 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
7936 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7937 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
7938 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
7939 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7940 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7941 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7942 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7946 Enables icsEnables[] = {
\r
7947 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7948 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7949 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7950 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7951 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7952 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7953 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7954 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7955 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7956 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7957 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7958 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7959 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7960 { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },
\r
7961 { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },
\r
7962 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7963 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7964 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7965 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7966 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
7971 Enables zippyEnables[] = {
\r
7972 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7973 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7974 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7975 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7980 Enables ncpEnables[] = {
\r
7981 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7982 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7983 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7984 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7985 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7986 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7987 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7988 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7989 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7990 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7991 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7992 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7993 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7994 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7995 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7996 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7997 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7998 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7999 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
8000 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
8001 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
8002 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
8006 Enables trainingOnEnables[] = {
\r
8007 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
8008 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
8009 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
8010 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
8011 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
8012 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
8013 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
8014 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8015 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
8019 Enables trainingOffEnables[] = {
\r
8020 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
8021 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
8022 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
8023 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
8024 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
8025 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
8026 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
8027 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8028 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
8032 /* These modify either ncpEnables or gnuEnables */
\r
8033 Enables cmailEnables[] = {
\r
8034 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
8035 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
8036 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
8037 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
8038 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
8039 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
8040 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
8044 Enables machineThinkingEnables[] = {
\r
8045 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8046 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
8047 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
8048 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8049 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
8050 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8051 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8052 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8053 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8054 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
8055 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8056 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8057 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8058 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8059 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
8060 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
8064 Enables userThinkingEnables[] = {
\r
8065 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8066 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
8067 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
8068 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8069 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
8070 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8071 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8072 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8073 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8074 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
8075 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
8076 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
8077 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
8078 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
8079 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
8080 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
8084 /*---------------------------------------------------------------------------*\
\r
8086 * Front-end interface functions exported by XBoard.
\r
8087 * Functions appear in same order as prototypes in frontend.h.
\r
8089 \*---------------------------------------------------------------------------*/
\r
8091 CheckMark(UINT item, int state)
\r
8093 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
8099 static UINT prevChecked = 0;
\r
8100 static int prevPausing = 0;
\r
8103 if (pausing != prevPausing) {
\r
8104 prevPausing = pausing;
\r
8105 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
8106 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
8107 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
8110 switch (gameMode) {
\r
8111 case BeginningOfGame:
\r
8112 if (appData.icsActive)
\r
8113 nowChecked = IDM_IcsClient;
\r
8114 else if (appData.noChessProgram)
\r
8115 nowChecked = IDM_EditGame;
\r
8117 nowChecked = IDM_MachineBlack;
\r
8119 case MachinePlaysBlack:
\r
8120 nowChecked = IDM_MachineBlack;
\r
8122 case MachinePlaysWhite:
\r
8123 nowChecked = IDM_MachineWhite;
\r
8125 case TwoMachinesPlay:
\r
8126 nowChecked = IDM_TwoMachines;
\r
8129 nowChecked = IDM_AnalysisMode;
\r
8132 nowChecked = IDM_AnalyzeFile;
\r
8135 nowChecked = IDM_EditGame;
\r
8137 case PlayFromGameFile:
\r
8138 nowChecked = IDM_LoadGame;
\r
8140 case EditPosition:
\r
8141 nowChecked = IDM_EditPosition;
\r
8144 nowChecked = IDM_Training;
\r
8146 case IcsPlayingWhite:
\r
8147 case IcsPlayingBlack:
\r
8148 case IcsObserving:
\r
8150 nowChecked = IDM_IcsClient;
\r
8157 CheckMark(prevChecked, MF_UNCHECKED);
\r
8158 CheckMark(nowChecked, MF_CHECKED);
\r
8159 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8161 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8162 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8163 MF_BYCOMMAND|MF_ENABLED);
\r
8165 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8166 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8169 prevChecked = nowChecked;
\r
8171 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8172 if (appData.icsActive) {
\r
8173 if (appData.icsEngineAnalyze) {
\r
8174 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8176 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8179 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8185 HMENU hmenu = GetMenu(hwndMain);
\r
8186 SetMenuEnables(hmenu, icsEnables);
\r
8187 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8188 MF_BYCOMMAND|MF_ENABLED);
\r
8190 if (appData.zippyPlay) {
\r
8191 SetMenuEnables(hmenu, zippyEnables);
\r
8192 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8193 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8194 MF_BYCOMMAND|MF_ENABLED);
\r
8202 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8208 HMENU hmenu = GetMenu(hwndMain);
\r
8209 SetMenuEnables(hmenu, ncpEnables);
\r
8210 DrawMenuBar(hwndMain);
\r
8216 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8220 SetTrainingModeOn()
\r
8223 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8224 for (i = 0; i < N_BUTTONS; i++) {
\r
8225 if (buttonDesc[i].hwnd != NULL)
\r
8226 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8231 VOID SetTrainingModeOff()
\r
8234 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8235 for (i = 0; i < N_BUTTONS; i++) {
\r
8236 if (buttonDesc[i].hwnd != NULL)
\r
8237 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8243 SetUserThinkingEnables()
\r
8245 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8249 SetMachineThinkingEnables()
\r
8251 HMENU hMenu = GetMenu(hwndMain);
\r
8252 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8254 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8256 if (gameMode == MachinePlaysBlack) {
\r
8257 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8258 } else if (gameMode == MachinePlaysWhite) {
\r
8259 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8260 } else if (gameMode == TwoMachinesPlay) {
\r
8261 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8267 DisplayTitle(char *str)
\r
8269 char title[MSG_SIZ], *host;
\r
8270 if (str[0] != NULLCHAR) {
\r
8271 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8272 } else if (appData.icsActive) {
\r
8273 if (appData.icsCommPort[0] != NULLCHAR)
\r
8276 host = appData.icsHost;
\r
8277 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8278 } else if (appData.noChessProgram) {
\r
8279 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8281 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8282 strcat(title, ": ");
\r
8283 strcat(title, first.tidy);
\r
8285 SetWindowText(hwndMain, title);
\r
8290 DisplayMessage(char *str1, char *str2)
\r
8294 int remain = MESSAGE_TEXT_MAX - 1;
\r
8297 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8298 messageText[0] = NULLCHAR;
\r
8300 len = strlen(str1);
\r
8301 if (len > remain) len = remain;
\r
8302 strncpy(messageText, str1, len);
\r
8303 messageText[len] = NULLCHAR;
\r
8306 if (*str2 && remain >= 2) {
\r
8308 strcat(messageText, " ");
\r
8311 len = strlen(str2);
\r
8312 if (len > remain) len = remain;
\r
8313 strncat(messageText, str2, len);
\r
8315 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8316 safeStrCpy(lastMsg, messageText, MSG_SIZ);
\r
8318 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8322 hdc = GetDC(hwndMain);
\r
8323 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8324 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8325 &messageRect, messageText, strlen(messageText), NULL);
\r
8326 (void) SelectObject(hdc, oldFont);
\r
8327 (void) ReleaseDC(hwndMain, hdc);
\r
8331 DisplayError(char *str, int error)
\r
8333 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8337 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8339 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8340 NULL, error, LANG_NEUTRAL,
\r
8341 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8343 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8345 ErrorMap *em = errmap;
\r
8346 while (em->err != 0 && em->err != error) em++;
\r
8347 if (em->err != 0) {
\r
8348 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8350 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8355 ErrorPopUp(_("Error"), buf);
\r
8360 DisplayMoveError(char *str)
\r
8362 fromX = fromY = -1;
\r
8363 ClearHighlights();
\r
8364 DrawPosition(FALSE, NULL);
\r
8365 if (appData.popupMoveErrors) {
\r
8366 ErrorPopUp(_("Error"), str);
\r
8368 DisplayMessage(str, "");
\r
8369 moveErrorMessageUp = TRUE;
\r
8374 DisplayFatalError(char *str, int error, int exitStatus)
\r
8376 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8378 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8381 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8382 NULL, error, LANG_NEUTRAL,
\r
8383 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8385 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8387 ErrorMap *em = errmap;
\r
8388 while (em->err != 0 && em->err != error) em++;
\r
8389 if (em->err != 0) {
\r
8390 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8392 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8397 if (appData.debugMode) {
\r
8398 fprintf(debugFP, "%s: %s\n", label, str);
\r
8400 if (appData.popupExitMessage) {
\r
8401 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8402 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8404 ExitEvent(exitStatus);
\r
8409 DisplayInformation(char *str)
\r
8411 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8416 DisplayNote(char *str)
\r
8418 ErrorPopUp(_("Note"), str);
\r
8423 char *title, *question, *replyPrefix;
\r
8428 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8430 static QuestionParams *qp;
\r
8431 char reply[MSG_SIZ];
\r
8434 switch (message) {
\r
8435 case WM_INITDIALOG:
\r
8436 qp = (QuestionParams *) lParam;
\r
8437 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8438 Translate(hDlg, DLG_Question);
\r
8439 SetWindowText(hDlg, qp->title);
\r
8440 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8441 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8445 switch (LOWORD(wParam)) {
\r
8447 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8448 if (*reply) strcat(reply, " ");
\r
8449 len = strlen(reply);
\r
8450 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8451 strcat(reply, "\n");
\r
8452 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8453 EndDialog(hDlg, TRUE);
\r
8454 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8457 EndDialog(hDlg, FALSE);
\r
8468 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8470 QuestionParams qp;
\r
8474 qp.question = question;
\r
8475 qp.replyPrefix = replyPrefix;
\r
8477 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8478 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8479 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8480 FreeProcInstance(lpProc);
\r
8483 /* [AS] Pick FRC position */
\r
8484 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8486 static int * lpIndexFRC;
\r
8492 case WM_INITDIALOG:
\r
8493 lpIndexFRC = (int *) lParam;
\r
8495 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8496 Translate(hDlg, DLG_NewGameFRC);
\r
8498 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8499 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8500 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8501 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8506 switch( LOWORD(wParam) ) {
\r
8508 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8509 EndDialog( hDlg, 0 );
\r
8510 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8513 EndDialog( hDlg, 1 );
\r
8515 case IDC_NFG_Edit:
\r
8516 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8517 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8519 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8522 case IDC_NFG_Random:
\r
8523 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8524 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8537 int index = appData.defaultFrcPosition;
\r
8538 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8540 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8542 if( result == 0 ) {
\r
8543 appData.defaultFrcPosition = index;
\r
8549 /* [AS] Game list options. Refactored by HGM */
\r
8551 HWND gameListOptionsDialog;
\r
8553 // low-level front-end: clear text edit / list widget
\r
8557 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8560 // low-level front-end: clear text edit / list widget
\r
8562 GLT_DeSelectList()
\r
8564 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8567 // low-level front-end: append line to text edit / list widget
\r
8569 GLT_AddToList( char *name )
\r
8572 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8576 // low-level front-end: get line from text edit / list widget
\r
8578 GLT_GetFromList( int index, char *name )
\r
8581 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8587 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8589 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8590 int idx2 = idx1 + delta;
\r
8591 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8593 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8596 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8597 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8598 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8599 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8603 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8607 case WM_INITDIALOG:
\r
8608 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8610 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8611 Translate(hDlg, DLG_GameListOptions);
\r
8613 /* Initialize list */
\r
8614 GLT_TagsToList( lpUserGLT );
\r
8616 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8621 switch( LOWORD(wParam) ) {
\r
8624 EndDialog( hDlg, 0 );
\r
8627 EndDialog( hDlg, 1 );
\r
8630 case IDC_GLT_Default:
\r
8631 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8634 case IDC_GLT_Restore:
\r
8635 GLT_TagsToList( appData.gameListTags );
\r
8639 GLT_MoveSelection( hDlg, -1 );
\r
8642 case IDC_GLT_Down:
\r
8643 GLT_MoveSelection( hDlg, +1 );
\r
8653 int GameListOptions()
\r
8656 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8658 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8660 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8662 if( result == 0 ) {
\r
8663 /* [AS] Memory leak here! */
\r
8664 appData.gameListTags = strdup( lpUserGLT );
\r
8671 DisplayIcsInteractionTitle(char *str)
\r
8673 char consoleTitle[MSG_SIZ];
\r
8675 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8676 SetWindowText(hwndConsole, consoleTitle);
\r
8678 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
8679 char buf[MSG_SIZ], *p = buf, *q;
\r
8680 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
8682 q = strchr(p, ';');
\r
8684 if(*p) ChatPopUp(p);
\r
8688 SetActiveWindow(hwndMain);
\r
8692 DrawPosition(int fullRedraw, Board board)
\r
8694 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8697 void NotifyFrontendLogin()
\r
8700 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8706 fromX = fromY = -1;
\r
8707 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8708 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8709 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8710 dragInfo.lastpos = dragInfo.pos;
\r
8711 dragInfo.start.x = dragInfo.start.y = -1;
\r
8712 dragInfo.from = dragInfo.start;
\r
8714 DrawPosition(TRUE, NULL);
\r
8721 CommentPopUp(char *title, char *str)
\r
8723 HWND hwnd = GetActiveWindow();
\r
8724 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8726 SetActiveWindow(hwnd);
\r
8730 CommentPopDown(void)
\r
8732 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8733 if (commentDialog) {
\r
8734 ShowWindow(commentDialog, SW_HIDE);
\r
8736 commentUp = FALSE;
\r
8740 EditCommentPopUp(int index, char *title, char *str)
\r
8742 EitherCommentPopUp(index, title, str, TRUE);
\r
8749 MyPlaySound(&sounds[(int)SoundMove]);
\r
8752 VOID PlayIcsWinSound()
\r
8754 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8757 VOID PlayIcsLossSound()
\r
8759 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8762 VOID PlayIcsDrawSound()
\r
8764 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8767 VOID PlayIcsUnfinishedSound()
\r
8769 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8775 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8781 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8789 consoleEcho = TRUE;
\r
8790 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8791 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8792 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8801 consoleEcho = FALSE;
\r
8802 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8803 /* This works OK: set text and background both to the same color */
\r
8805 cf.crTextColor = COLOR_ECHOOFF;
\r
8806 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8807 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8810 /* No Raw()...? */
\r
8812 void Colorize(ColorClass cc, int continuation)
\r
8814 currentColorClass = cc;
\r
8815 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8816 consoleCF.crTextColor = textAttribs[cc].color;
\r
8817 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8818 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8824 static char buf[MSG_SIZ];
\r
8825 DWORD bufsiz = MSG_SIZ;
\r
8827 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8828 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8830 if (!GetUserName(buf, &bufsiz)) {
\r
8831 /*DisplayError("Error getting user name", GetLastError());*/
\r
8832 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8840 static char buf[MSG_SIZ];
\r
8841 DWORD bufsiz = MSG_SIZ;
\r
8843 if (!GetComputerName(buf, &bufsiz)) {
\r
8844 /*DisplayError("Error getting host name", GetLastError());*/
\r
8845 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8852 ClockTimerRunning()
\r
8854 return clockTimerEvent != 0;
\r
8860 if (clockTimerEvent == 0) return FALSE;
\r
8861 KillTimer(hwndMain, clockTimerEvent);
\r
8862 clockTimerEvent = 0;
\r
8867 StartClockTimer(long millisec)
\r
8869 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8870 (UINT) millisec, NULL);
\r
8874 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8877 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8879 if(appData.noGUI) return;
\r
8880 hdc = GetDC(hwndMain);
\r
8881 if (!IsIconic(hwndMain)) {
\r
8882 DisplayAClock(hdc, timeRemaining, highlight,
\r
8883 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8885 if (highlight && iconCurrent == iconBlack) {
\r
8886 iconCurrent = iconWhite;
\r
8887 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8888 if (IsIconic(hwndMain)) {
\r
8889 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8892 (void) ReleaseDC(hwndMain, hdc);
\r
8894 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8898 DisplayBlackClock(long timeRemaining, int highlight)
\r
8901 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8903 if(appData.noGUI) return;
\r
8904 hdc = GetDC(hwndMain);
\r
8905 if (!IsIconic(hwndMain)) {
\r
8906 DisplayAClock(hdc, timeRemaining, highlight,
\r
8907 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8909 if (highlight && iconCurrent == iconWhite) {
\r
8910 iconCurrent = iconBlack;
\r
8911 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8912 if (IsIconic(hwndMain)) {
\r
8913 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8916 (void) ReleaseDC(hwndMain, hdc);
\r
8918 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8923 LoadGameTimerRunning()
\r
8925 return loadGameTimerEvent != 0;
\r
8929 StopLoadGameTimer()
\r
8931 if (loadGameTimerEvent == 0) return FALSE;
\r
8932 KillTimer(hwndMain, loadGameTimerEvent);
\r
8933 loadGameTimerEvent = 0;
\r
8938 StartLoadGameTimer(long millisec)
\r
8940 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8941 (UINT) millisec, NULL);
\r
8949 char fileTitle[MSG_SIZ];
\r
8951 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8952 f = OpenFileDialog(hwndMain, "a", defName,
\r
8953 appData.oldSaveStyle ? "gam" : "pgn",
\r
8955 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8957 SaveGame(f, 0, "");
\r
8964 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8966 if (delayedTimerEvent != 0) {
\r
8967 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8968 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8970 KillTimer(hwndMain, delayedTimerEvent);
\r
8971 delayedTimerEvent = 0;
\r
8972 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8973 delayedTimerCallback();
\r
8975 delayedTimerCallback = cb;
\r
8976 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8977 (UINT) millisec, NULL);
\r
8980 DelayedEventCallback
\r
8983 if (delayedTimerEvent) {
\r
8984 return delayedTimerCallback;
\r
8991 CancelDelayedEvent()
\r
8993 if (delayedTimerEvent) {
\r
8994 KillTimer(hwndMain, delayedTimerEvent);
\r
8995 delayedTimerEvent = 0;
\r
8999 DWORD GetWin32Priority(int nice)
\r
9000 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
9002 REALTIME_PRIORITY_CLASS 0x00000100
\r
9003 HIGH_PRIORITY_CLASS 0x00000080
\r
9004 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
9005 NORMAL_PRIORITY_CLASS 0x00000020
\r
9006 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
9007 IDLE_PRIORITY_CLASS 0x00000040
\r
9009 if (nice < -15) return 0x00000080;
\r
9010 if (nice < 0) return 0x00008000;
\r
9011 if (nice == 0) return 0x00000020;
\r
9012 if (nice < 15) return 0x00004000;
\r
9013 return 0x00000040;
\r
9016 void RunCommand(char *cmdLine)
\r
9018 /* Now create the child process. */
\r
9019 STARTUPINFO siStartInfo;
\r
9020 PROCESS_INFORMATION piProcInfo;
\r
9022 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9023 siStartInfo.lpReserved = NULL;
\r
9024 siStartInfo.lpDesktop = NULL;
\r
9025 siStartInfo.lpTitle = NULL;
\r
9026 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9027 siStartInfo.cbReserved2 = 0;
\r
9028 siStartInfo.lpReserved2 = NULL;
\r
9029 siStartInfo.hStdInput = NULL;
\r
9030 siStartInfo.hStdOutput = NULL;
\r
9031 siStartInfo.hStdError = NULL;
\r
9033 CreateProcess(NULL,
\r
9034 cmdLine, /* command line */
\r
9035 NULL, /* process security attributes */
\r
9036 NULL, /* primary thread security attrs */
\r
9037 TRUE, /* handles are inherited */
\r
9038 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9039 NULL, /* use parent's environment */
\r
9041 &siStartInfo, /* STARTUPINFO pointer */
\r
9042 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9044 CloseHandle(piProcInfo.hThread);
\r
9047 /* Start a child process running the given program.
\r
9048 The process's standard output can be read from "from", and its
\r
9049 standard input can be written to "to".
\r
9050 Exit with fatal error if anything goes wrong.
\r
9051 Returns an opaque pointer that can be used to destroy the process
\r
9055 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
9057 #define BUFSIZE 4096
\r
9059 HANDLE hChildStdinRd, hChildStdinWr,
\r
9060 hChildStdoutRd, hChildStdoutWr;
\r
9061 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
9062 SECURITY_ATTRIBUTES saAttr;
\r
9064 PROCESS_INFORMATION piProcInfo;
\r
9065 STARTUPINFO siStartInfo;
\r
9067 char buf[MSG_SIZ];
\r
9070 if (appData.debugMode) {
\r
9071 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
9076 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
9077 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
9078 saAttr.bInheritHandle = TRUE;
\r
9079 saAttr.lpSecurityDescriptor = NULL;
\r
9082 * The steps for redirecting child's STDOUT:
\r
9083 * 1. Create anonymous pipe to be STDOUT for child.
\r
9084 * 2. Create a noninheritable duplicate of read handle,
\r
9085 * and close the inheritable read handle.
\r
9088 /* Create a pipe for the child's STDOUT. */
\r
9089 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
9090 return GetLastError();
\r
9093 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
9094 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
9095 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
9096 FALSE, /* not inherited */
\r
9097 DUPLICATE_SAME_ACCESS);
\r
9099 return GetLastError();
\r
9101 CloseHandle(hChildStdoutRd);
\r
9104 * The steps for redirecting child's STDIN:
\r
9105 * 1. Create anonymous pipe to be STDIN for child.
\r
9106 * 2. Create a noninheritable duplicate of write handle,
\r
9107 * and close the inheritable write handle.
\r
9110 /* Create a pipe for the child's STDIN. */
\r
9111 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
9112 return GetLastError();
\r
9115 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
9116 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
9117 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
9118 FALSE, /* not inherited */
\r
9119 DUPLICATE_SAME_ACCESS);
\r
9121 return GetLastError();
\r
9123 CloseHandle(hChildStdinWr);
\r
9125 /* Arrange to (1) look in dir for the child .exe file, and
\r
9126 * (2) have dir be the child's working directory. Interpret
\r
9127 * dir relative to the directory WinBoard loaded from. */
\r
9128 GetCurrentDirectory(MSG_SIZ, buf);
\r
9129 SetCurrentDirectory(installDir);
\r
9130 SetCurrentDirectory(dir);
\r
9132 /* Now create the child process. */
\r
9134 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9135 siStartInfo.lpReserved = NULL;
\r
9136 siStartInfo.lpDesktop = NULL;
\r
9137 siStartInfo.lpTitle = NULL;
\r
9138 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9139 siStartInfo.cbReserved2 = 0;
\r
9140 siStartInfo.lpReserved2 = NULL;
\r
9141 siStartInfo.hStdInput = hChildStdinRd;
\r
9142 siStartInfo.hStdOutput = hChildStdoutWr;
\r
9143 siStartInfo.hStdError = hChildStdoutWr;
\r
9145 fSuccess = CreateProcess(NULL,
\r
9146 cmdLine, /* command line */
\r
9147 NULL, /* process security attributes */
\r
9148 NULL, /* primary thread security attrs */
\r
9149 TRUE, /* handles are inherited */
\r
9150 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9151 NULL, /* use parent's environment */
\r
9153 &siStartInfo, /* STARTUPINFO pointer */
\r
9154 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9156 err = GetLastError();
\r
9157 SetCurrentDirectory(buf); /* return to prev directory */
\r
9162 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
9163 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
9164 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
9167 /* Close the handles we don't need in the parent */
\r
9168 CloseHandle(piProcInfo.hThread);
\r
9169 CloseHandle(hChildStdinRd);
\r
9170 CloseHandle(hChildStdoutWr);
\r
9172 /* Prepare return value */
\r
9173 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9174 cp->kind = CPReal;
\r
9175 cp->hProcess = piProcInfo.hProcess;
\r
9176 cp->pid = piProcInfo.dwProcessId;
\r
9177 cp->hFrom = hChildStdoutRdDup;
\r
9178 cp->hTo = hChildStdinWrDup;
\r
9180 *pr = (void *) cp;
\r
9182 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
9183 2000 where engines sometimes don't see the initial command(s)
\r
9184 from WinBoard and hang. I don't understand how that can happen,
\r
9185 but the Sleep is harmless, so I've put it in. Others have also
\r
9186 reported what may be the same problem, so hopefully this will fix
\r
9187 it for them too. */
\r
9195 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
9197 ChildProc *cp; int result;
\r
9199 cp = (ChildProc *) pr;
\r
9200 if (cp == NULL) return;
\r
9202 switch (cp->kind) {
\r
9204 /* TerminateProcess is considered harmful, so... */
\r
9205 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9206 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9207 /* The following doesn't work because the chess program
\r
9208 doesn't "have the same console" as WinBoard. Maybe
\r
9209 we could arrange for this even though neither WinBoard
\r
9210 nor the chess program uses a console for stdio? */
\r
9211 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9213 /* [AS] Special termination modes for misbehaving programs... */
\r
9214 if( signal == 9 ) {
\r
9215 result = TerminateProcess( cp->hProcess, 0 );
\r
9217 if ( appData.debugMode) {
\r
9218 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9221 else if( signal == 10 ) {
\r
9222 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
9224 if( dw != WAIT_OBJECT_0 ) {
\r
9225 result = TerminateProcess( cp->hProcess, 0 );
\r
9227 if ( appData.debugMode) {
\r
9228 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9234 CloseHandle(cp->hProcess);
\r
9238 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9242 closesocket(cp->sock);
\r
9247 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9248 closesocket(cp->sock);
\r
9249 closesocket(cp->sock2);
\r
9257 InterruptChildProcess(ProcRef pr)
\r
9261 cp = (ChildProc *) pr;
\r
9262 if (cp == NULL) return;
\r
9263 switch (cp->kind) {
\r
9265 /* The following doesn't work because the chess program
\r
9266 doesn't "have the same console" as WinBoard. Maybe
\r
9267 we could arrange for this even though neither WinBoard
\r
9268 nor the chess program uses a console for stdio */
\r
9269 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9274 /* Can't interrupt */
\r
9278 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9285 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9287 char cmdLine[MSG_SIZ];
\r
9289 if (port[0] == NULLCHAR) {
\r
9290 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9292 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9294 return StartChildProcess(cmdLine, "", pr);
\r
9298 /* Code to open TCP sockets */
\r
9301 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9307 struct sockaddr_in sa, mysa;
\r
9308 struct hostent FAR *hp;
\r
9309 unsigned short uport;
\r
9310 WORD wVersionRequested;
\r
9313 /* Initialize socket DLL */
\r
9314 wVersionRequested = MAKEWORD(1, 1);
\r
9315 err = WSAStartup(wVersionRequested, &wsaData);
\r
9316 if (err != 0) return err;
\r
9319 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9320 err = WSAGetLastError();
\r
9325 /* Bind local address using (mostly) don't-care values.
\r
9327 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9328 mysa.sin_family = AF_INET;
\r
9329 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9330 uport = (unsigned short) 0;
\r
9331 mysa.sin_port = htons(uport);
\r
9332 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9333 == SOCKET_ERROR) {
\r
9334 err = WSAGetLastError();
\r
9339 /* Resolve remote host name */
\r
9340 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9341 if (!(hp = gethostbyname(host))) {
\r
9342 unsigned int b0, b1, b2, b3;
\r
9344 err = WSAGetLastError();
\r
9346 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9347 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9348 hp->h_addrtype = AF_INET;
\r
9350 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9351 hp->h_addr_list[0] = (char *) malloc(4);
\r
9352 hp->h_addr_list[0][0] = (char) b0;
\r
9353 hp->h_addr_list[0][1] = (char) b1;
\r
9354 hp->h_addr_list[0][2] = (char) b2;
\r
9355 hp->h_addr_list[0][3] = (char) b3;
\r
9361 sa.sin_family = hp->h_addrtype;
\r
9362 uport = (unsigned short) atoi(port);
\r
9363 sa.sin_port = htons(uport);
\r
9364 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9366 /* Make connection */
\r
9367 if (connect(s, (struct sockaddr *) &sa,
\r
9368 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9369 err = WSAGetLastError();
\r
9374 /* Prepare return value */
\r
9375 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9376 cp->kind = CPSock;
\r
9378 *pr = (ProcRef *) cp;
\r
9384 OpenCommPort(char *name, ProcRef *pr)
\r
9389 char fullname[MSG_SIZ];
\r
9391 if (*name != '\\')
\r
9392 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9394 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9396 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9397 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9398 if (h == (HANDLE) -1) {
\r
9399 return GetLastError();
\r
9403 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9405 /* Accumulate characters until a 100ms pause, then parse */
\r
9406 ct.ReadIntervalTimeout = 100;
\r
9407 ct.ReadTotalTimeoutMultiplier = 0;
\r
9408 ct.ReadTotalTimeoutConstant = 0;
\r
9409 ct.WriteTotalTimeoutMultiplier = 0;
\r
9410 ct.WriteTotalTimeoutConstant = 0;
\r
9411 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9413 /* Prepare return value */
\r
9414 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9415 cp->kind = CPComm;
\r
9418 *pr = (ProcRef *) cp;
\r
9424 OpenLoopback(ProcRef *pr)
\r
9426 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9432 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9437 struct sockaddr_in sa, mysa;
\r
9438 struct hostent FAR *hp;
\r
9439 unsigned short uport;
\r
9440 WORD wVersionRequested;
\r
9443 char stderrPortStr[MSG_SIZ];
\r
9445 /* Initialize socket DLL */
\r
9446 wVersionRequested = MAKEWORD(1, 1);
\r
9447 err = WSAStartup(wVersionRequested, &wsaData);
\r
9448 if (err != 0) return err;
\r
9450 /* Resolve remote host name */
\r
9451 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9452 if (!(hp = gethostbyname(host))) {
\r
9453 unsigned int b0, b1, b2, b3;
\r
9455 err = WSAGetLastError();
\r
9457 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9458 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9459 hp->h_addrtype = AF_INET;
\r
9461 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9462 hp->h_addr_list[0] = (char *) malloc(4);
\r
9463 hp->h_addr_list[0][0] = (char) b0;
\r
9464 hp->h_addr_list[0][1] = (char) b1;
\r
9465 hp->h_addr_list[0][2] = (char) b2;
\r
9466 hp->h_addr_list[0][3] = (char) b3;
\r
9472 sa.sin_family = hp->h_addrtype;
\r
9473 uport = (unsigned short) 514;
\r
9474 sa.sin_port = htons(uport);
\r
9475 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9477 /* Bind local socket to unused "privileged" port address
\r
9479 s = INVALID_SOCKET;
\r
9480 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9481 mysa.sin_family = AF_INET;
\r
9482 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9483 for (fromPort = 1023;; fromPort--) {
\r
9484 if (fromPort < 0) {
\r
9486 return WSAEADDRINUSE;
\r
9488 if (s == INVALID_SOCKET) {
\r
9489 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9490 err = WSAGetLastError();
\r
9495 uport = (unsigned short) fromPort;
\r
9496 mysa.sin_port = htons(uport);
\r
9497 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9498 == SOCKET_ERROR) {
\r
9499 err = WSAGetLastError();
\r
9500 if (err == WSAEADDRINUSE) continue;
\r
9504 if (connect(s, (struct sockaddr *) &sa,
\r
9505 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9506 err = WSAGetLastError();
\r
9507 if (err == WSAEADDRINUSE) {
\r
9518 /* Bind stderr local socket to unused "privileged" port address
\r
9520 s2 = INVALID_SOCKET;
\r
9521 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9522 mysa.sin_family = AF_INET;
\r
9523 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9524 for (fromPort = 1023;; fromPort--) {
\r
9525 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9526 if (fromPort < 0) {
\r
9527 (void) closesocket(s);
\r
9529 return WSAEADDRINUSE;
\r
9531 if (s2 == INVALID_SOCKET) {
\r
9532 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9533 err = WSAGetLastError();
\r
9539 uport = (unsigned short) fromPort;
\r
9540 mysa.sin_port = htons(uport);
\r
9541 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9542 == SOCKET_ERROR) {
\r
9543 err = WSAGetLastError();
\r
9544 if (err == WSAEADDRINUSE) continue;
\r
9545 (void) closesocket(s);
\r
9549 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9550 err = WSAGetLastError();
\r
9551 if (err == WSAEADDRINUSE) {
\r
9553 s2 = INVALID_SOCKET;
\r
9556 (void) closesocket(s);
\r
9557 (void) closesocket(s2);
\r
9563 prevStderrPort = fromPort; // remember port used
\r
9564 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9566 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9567 err = WSAGetLastError();
\r
9568 (void) closesocket(s);
\r
9569 (void) closesocket(s2);
\r
9574 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9575 err = WSAGetLastError();
\r
9576 (void) closesocket(s);
\r
9577 (void) closesocket(s2);
\r
9581 if (*user == NULLCHAR) user = UserName();
\r
9582 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9583 err = WSAGetLastError();
\r
9584 (void) closesocket(s);
\r
9585 (void) closesocket(s2);
\r
9589 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9590 err = WSAGetLastError();
\r
9591 (void) closesocket(s);
\r
9592 (void) closesocket(s2);
\r
9597 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9598 err = WSAGetLastError();
\r
9599 (void) closesocket(s);
\r
9600 (void) closesocket(s2);
\r
9604 (void) closesocket(s2); /* Stop listening */
\r
9606 /* Prepare return value */
\r
9607 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9608 cp->kind = CPRcmd;
\r
9611 *pr = (ProcRef *) cp;
\r
9618 AddInputSource(ProcRef pr, int lineByLine,
\r
9619 InputCallback func, VOIDSTAR closure)
\r
9621 InputSource *is, *is2 = NULL;
\r
9622 ChildProc *cp = (ChildProc *) pr;
\r
9624 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9625 is->lineByLine = lineByLine;
\r
9627 is->closure = closure;
\r
9628 is->second = NULL;
\r
9629 is->next = is->buf;
\r
9630 if (pr == NoProc) {
\r
9631 is->kind = CPReal;
\r
9632 consoleInputSource = is;
\r
9634 is->kind = cp->kind;
\r
9636 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9637 we create all threads suspended so that the is->hThread variable can be
\r
9638 safely assigned, then let the threads start with ResumeThread.
\r
9640 switch (cp->kind) {
\r
9642 is->hFile = cp->hFrom;
\r
9643 cp->hFrom = NULL; /* now owned by InputThread */
\r
9645 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9646 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9650 is->hFile = cp->hFrom;
\r
9651 cp->hFrom = NULL; /* now owned by InputThread */
\r
9653 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9654 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9658 is->sock = cp->sock;
\r
9660 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9661 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9665 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9667 is->sock = cp->sock;
\r
9669 is2->sock = cp->sock2;
\r
9670 is2->second = is2;
\r
9672 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9673 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9675 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9676 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9680 if( is->hThread != NULL ) {
\r
9681 ResumeThread( is->hThread );
\r
9684 if( is2 != NULL && is2->hThread != NULL ) {
\r
9685 ResumeThread( is2->hThread );
\r
9689 return (InputSourceRef) is;
\r
9693 RemoveInputSource(InputSourceRef isr)
\r
9697 is = (InputSource *) isr;
\r
9698 is->hThread = NULL; /* tell thread to stop */
\r
9699 CloseHandle(is->hThread);
\r
9700 if (is->second != NULL) {
\r
9701 is->second->hThread = NULL;
\r
9702 CloseHandle(is->second->hThread);
\r
9706 int no_wrap(char *message, int count)
\r
9708 ConsoleOutput(message, count, FALSE);
\r
9713 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9716 int outCount = SOCKET_ERROR;
\r
9717 ChildProc *cp = (ChildProc *) pr;
\r
9718 static OVERLAPPED ovl;
\r
9719 static int line = 0;
\r
9723 if (appData.noJoin || !appData.useInternalWrap)
\r
9724 return no_wrap(message, count);
\r
9727 int width = get_term_width();
\r
9728 int len = wrap(NULL, message, count, width, &line);
\r
9729 char *msg = malloc(len);
\r
9733 return no_wrap(message, count);
\r
9736 dbgchk = wrap(msg, message, count, width, &line);
\r
9737 if (dbgchk != len && appData.debugMode)
\r
9738 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9739 ConsoleOutput(msg, len, FALSE);
\r
9746 if (ovl.hEvent == NULL) {
\r
9747 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9749 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9751 switch (cp->kind) {
\r
9754 outCount = send(cp->sock, message, count, 0);
\r
9755 if (outCount == SOCKET_ERROR) {
\r
9756 *outError = WSAGetLastError();
\r
9758 *outError = NO_ERROR;
\r
9763 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9764 &dOutCount, NULL)) {
\r
9765 *outError = NO_ERROR;
\r
9766 outCount = (int) dOutCount;
\r
9768 *outError = GetLastError();
\r
9773 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9774 &dOutCount, &ovl);
\r
9775 if (*outError == NO_ERROR) {
\r
9776 outCount = (int) dOutCount;
\r
9786 if(n != 0) Sleep(n);
\r
9790 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9793 /* Ignore delay, not implemented for WinBoard */
\r
9794 return OutputToProcess(pr, message, count, outError);
\r
9799 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9800 char *buf, int count, int error)
\r
9802 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9805 /* see wgamelist.c for Game List functions */
\r
9806 /* see wedittags.c for Edit Tags functions */
\r
9813 char buf[MSG_SIZ];
\r
9816 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9817 f = fopen(buf, "r");
\r
9819 ProcessICSInitScript(f);
\r
9829 StartAnalysisClock()
\r
9831 if (analysisTimerEvent) return;
\r
9832 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9833 (UINT) 2000, NULL);
\r
9837 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9839 highlightInfo.sq[0].x = fromX;
\r
9840 highlightInfo.sq[0].y = fromY;
\r
9841 highlightInfo.sq[1].x = toX;
\r
9842 highlightInfo.sq[1].y = toY;
\r
9848 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9849 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9853 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9855 premoveHighlightInfo.sq[0].x = fromX;
\r
9856 premoveHighlightInfo.sq[0].y = fromY;
\r
9857 premoveHighlightInfo.sq[1].x = toX;
\r
9858 premoveHighlightInfo.sq[1].y = toY;
\r
9862 ClearPremoveHighlights()
\r
9864 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9865 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9869 ShutDownFrontEnd()
\r
9871 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9872 DeleteClipboardTempFiles();
\r
9878 if (IsIconic(hwndMain))
\r
9879 ShowWindow(hwndMain, SW_RESTORE);
\r
9881 SetActiveWindow(hwndMain);
\r
9885 * Prototypes for animation support routines
\r
9887 static void ScreenSquare(int column, int row, POINT * pt);
\r
9888 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9889 POINT frames[], int * nFrames);
\r
9895 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9896 { // [HGM] atomic: animate blast wave
\r
9899 explodeInfo.fromX = fromX;
\r
9900 explodeInfo.fromY = fromY;
\r
9901 explodeInfo.toX = toX;
\r
9902 explodeInfo.toY = toY;
\r
9903 for(i=1; i<4*kFactor; i++) {
\r
9904 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9905 DrawPosition(FALSE, board);
\r
9906 Sleep(appData.animSpeed);
\r
9908 explodeInfo.radius = 0;
\r
9909 DrawPosition(TRUE, board);
\r
9913 AnimateMove(board, fromX, fromY, toX, toY)
\r
9920 ChessSquare piece;
\r
9921 POINT start, finish, mid;
\r
9922 POINT frames[kFactor * 2 + 1];
\r
9925 if (!appData.animate) return;
\r
9926 if (doingSizing) return;
\r
9927 if (fromY < 0 || fromX < 0) return;
\r
9928 piece = board[fromY][fromX];
\r
9929 if (piece >= EmptySquare) return;
\r
9931 ScreenSquare(fromX, fromY, &start);
\r
9932 ScreenSquare(toX, toY, &finish);
\r
9934 /* All moves except knight jumps move in straight line */
\r
9935 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9936 mid.x = start.x + (finish.x - start.x) / 2;
\r
9937 mid.y = start.y + (finish.y - start.y) / 2;
\r
9939 /* Knight: make straight movement then diagonal */
\r
9940 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9941 mid.x = start.x + (finish.x - start.x) / 2;
\r
9945 mid.y = start.y + (finish.y - start.y) / 2;
\r
9949 /* Don't use as many frames for very short moves */
\r
9950 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9951 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9953 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9955 animInfo.from.x = fromX;
\r
9956 animInfo.from.y = fromY;
\r
9957 animInfo.to.x = toX;
\r
9958 animInfo.to.y = toY;
\r
9959 animInfo.lastpos = start;
\r
9960 animInfo.piece = piece;
\r
9961 for (n = 0; n < nFrames; n++) {
\r
9962 animInfo.pos = frames[n];
\r
9963 DrawPosition(FALSE, NULL);
\r
9964 animInfo.lastpos = animInfo.pos;
\r
9965 Sleep(appData.animSpeed);
\r
9967 animInfo.pos = finish;
\r
9968 DrawPosition(FALSE, NULL);
\r
9969 animInfo.piece = EmptySquare;
\r
9970 Explode(board, fromX, fromY, toX, toY);
\r
9973 /* Convert board position to corner of screen rect and color */
\r
9976 ScreenSquare(column, row, pt)
\r
9977 int column; int row; POINT * pt;
\r
9980 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
9981 pt->y = lineGap + row * (squareSize + lineGap) + border;
\r
9983 pt->x = lineGap + column * (squareSize + lineGap) + border;
\r
9984 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
9988 /* Generate a series of frame coords from start->mid->finish.
\r
9989 The movement rate doubles until the half way point is
\r
9990 reached, then halves back down to the final destination,
\r
9991 which gives a nice slow in/out effect. The algorithmn
\r
9992 may seem to generate too many intermediates for short
\r
9993 moves, but remember that the purpose is to attract the
\r
9994 viewers attention to the piece about to be moved and
\r
9995 then to where it ends up. Too few frames would be less
\r
9999 Tween(start, mid, finish, factor, frames, nFrames)
\r
10000 POINT * start; POINT * mid;
\r
10001 POINT * finish; int factor;
\r
10002 POINT frames[]; int * nFrames;
\r
10004 int n, fraction = 1, count = 0;
\r
10006 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
10007 for (n = 0; n < factor; n++)
\r
10009 for (n = 0; n < factor; n++) {
\r
10010 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
10011 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
10013 fraction = fraction / 2;
\r
10017 frames[count] = *mid;
\r
10020 /* Slow out, stepping 1/2, then 1/4, ... */
\r
10022 for (n = 0; n < factor; n++) {
\r
10023 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
10024 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
10026 fraction = fraction * 2;
\r
10028 *nFrames = count;
\r
10032 SettingsPopUp(ChessProgramState *cps)
\r
10033 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
10034 EngineOptionsPopup(savedHwnd, cps);
\r
10037 int flock(int fid, int code)
\r
10039 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
10041 ov.hEvent = NULL;
\r
10043 ov.OffsetHigh = 0;
\r
10045 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
10046 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
10047 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
10048 default: return -1;
\r
10057 static char col[8][20];
\r
10058 COLORREF color = *(COLORREF *) colorVariable[n];
\r
10060 snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
10065 ActivateTheme (int new)
\r
10066 { // Redo initialization of features depending on options that can occur in themes
\r
10068 if(new) InitDrawingColors();
\r
10069 fontBitmapSquareSize = 0; // request creation of new font pieces
\r
10070 InitDrawingSizes(boardSize, 0);
\r
10071 InvalidateRect(hwndMain, NULL, TRUE);
\r