2 * WinBoard.c -- Windows NT front end to XBoard
\r
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
\r
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
\r
8 * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 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 void ChatPopUp P((char *s));
\r
117 ChessSquare piece;
\r
118 POINT pos; /* window coordinates of current pos */
\r
119 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
120 POINT from; /* board coordinates of the piece's orig pos */
\r
121 POINT to; /* board coordinates of the piece's new pos */
\r
124 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
127 POINT start; /* window coordinates of start pos */
\r
128 POINT pos; /* window coordinates of current pos */
\r
129 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
130 POINT from; /* board coordinates of the piece's orig pos */
\r
134 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };
\r
137 POINT sq[2]; /* board coordinates of from, to squares */
\r
140 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
141 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
142 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
143 static HighlightInfo oldPartnerHighlight = { {{-1, -1}, {-1, -1}} };
\r
145 typedef struct { // [HGM] atomic
\r
146 int fromX, fromY, toX, toY, radius;
\r
149 static ExplodeInfo explodeInfo;
\r
151 /* Window class names */
\r
152 char szAppName[] = "WinBoard";
\r
153 char szConsoleName[] = "WBConsole";
\r
155 /* Title bar text */
\r
156 char szTitle[] = "WinBoard";
\r
157 char szConsoleTitle[] = "I C S Interaction";
\r
160 char *settingsFileName;
\r
161 Boolean saveSettingsOnExit;
\r
162 char installDir[MSG_SIZ];
\r
163 int errorExitStatus;
\r
165 BoardSize boardSize;
\r
166 Boolean chessProgram;
\r
167 //static int boardX, boardY;
\r
168 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
169 int squareSize, lineGap, minorSize, border;
\r
170 static int winW, winH;
\r
171 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
172 static int logoHeight = 0;
\r
173 static char messageText[MESSAGE_TEXT_MAX];
\r
174 static int clockTimerEvent = 0;
\r
175 static int loadGameTimerEvent = 0;
\r
176 static int analysisTimerEvent = 0;
\r
177 static DelayedEventCallback delayedTimerCallback;
\r
178 static int delayedTimerEvent = 0;
\r
179 static int buttonCount = 2;
\r
180 char *icsTextMenuString;
\r
182 char *firstChessProgramNames;
\r
183 char *secondChessProgramNames;
\r
185 #define PALETTESIZE 256
\r
187 HINSTANCE hInst; /* current instance */
\r
188 Boolean alwaysOnTop = FALSE;
\r
190 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
191 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
192 COLORREF markerColor[8] = { 0x00FFFF, 0x0000FF, 0x00FF00, 0xFF0000, 0xFFFF00, 0xFF00FF, 0xFFFFFF, 0x000000 };
\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[8], /* [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
229 #if defined(_winmajor)
\r
230 #define oldDialog (_winmajor < 4)
\r
232 #define oldDialog 0
\r
236 #define INTERNATIONAL
\r
238 #ifdef INTERNATIONAL
\r
239 # define _(s) T_(s)
\r
245 # define Translate(x, y)
\r
246 # define LoadLanguageFile(s)
\r
249 #ifdef INTERNATIONAL
\r
251 Boolean barbaric; // flag indicating if translation is needed
\r
253 // list of item numbers used in each dialog (used to alter language at run time)
\r
255 #define ABOUTBOX -1 /* not sure why these are needed */
\r
256 #define ABOUTBOX2 -1
\r
258 int dialogItems[][42] = {
\r
259 { ABOUTBOX, IDOK, OPT_MESS, 400 },
\r
260 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed,
\r
261 OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors, IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL },
\r
262 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,
\r
263 OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds,
\r
264 OPT_Ranget, IDOK, IDCANCEL },
\r
265 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
266 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
267 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
268 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
269 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
270 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
271 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
272 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
273 { ABOUTBOX2, IDC_ChessBoard },
\r
274 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
275 OPT_GameListClose, IDC_GameListDoFilter },
\r
276 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
277 { DLG_Error, IDOK },
\r
278 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
279 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
280 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
281 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
282 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
283 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
284 { DLG_IndexNumber, IDC_Index },
\r
285 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
286 { DLG_TypeInName, IDOK, IDCANCEL },
\r
287 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
288 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
289 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
290 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
291 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
292 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
293 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
294 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
295 OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove },
\r
296 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
297 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
298 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
299 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
300 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
301 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
302 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
303 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
304 GPB_General, GPB_Alarm, OPT_AutoCreate },
\r
305 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
306 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
307 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
308 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
309 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
310 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
311 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
312 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid },
\r
313 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
314 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
315 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
316 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
317 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
318 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
319 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,
\r
320 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
321 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
322 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
323 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
324 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,
\r
325 OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7,
\r
326 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
327 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
328 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
329 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
330 { DLG_MoveHistory },
\r
331 { DLG_EvalGraph },
\r
332 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
333 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
334 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
335 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
336 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
337 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
338 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
339 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
340 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
344 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];
\r
345 static int lastChecked;
\r
346 static char oldLanguage[MSG_SIZ], *menuText[10][30];
\r
347 extern int tinyLayout;
\r
348 extern char * menuBarText[][10];
\r
351 LoadLanguageFile(char *name)
\r
352 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
354 int i=0, j=0, n=0, k;
\r
357 if(!name || name[0] == NULLCHAR) return;
\r
358 snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension
\r
359 appData.language = oldLanguage;
\r
360 if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on
\r
361 if((f = fopen(buf, "r")) == NULL) return;
\r
362 while((k = fgetc(f)) != EOF) {
\r
363 if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }
\r
364 languageBuf[i] = k;
\r
366 if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {
\r
368 if(p = strstr(languageBuf + n + 1, "\" === \"")) {
\r
369 if(p > languageBuf+n+2 && p+8 < languageBuf+i) {
\r
370 if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }
\r
371 english[j] = languageBuf + n + 1; *p = 0;
\r
372 foreign[j++] = p + 7; languageBuf[i-1] = 0;
\r
373 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);
\r
378 } else if(i > 0 && languageBuf[i-1] == '\\') {
\r
380 case 'n': k = '\n'; break;
\r
381 case 'r': k = '\r'; break;
\r
382 case 't': k = '\t'; break;
\r
384 languageBuf[--i] = k;
\r
389 barbaric = (j != 0);
\r
390 safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );
\r
395 { // return the translation of the given string
\r
396 // efficiency can be improved a lot...
\r
398 static char buf[MSG_SIZ];
\r
399 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
400 if(!barbaric) return s;
\r
401 if(!s) return ""; // sanity
\r
402 while(english[i]) {
\r
403 if(!strcmp(s, english[i])) return foreign[i];
\r
404 if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending
\r
405 snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion
\r
414 Translate(HWND hDlg, int dialogID)
\r
415 { // translate all text items in the given dialog
\r
417 char buf[MSG_SIZ], *s;
\r
418 if(!barbaric) return;
\r
419 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
420 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
421 GetWindowText( hDlg, buf, MSG_SIZ );
\r
423 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
424 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
425 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
426 if(strlen(buf) == 0) continue;
\r
428 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
433 TranslateOneMenu(int i, HMENU subMenu)
\r
436 static MENUITEMINFO info;
\r
438 info.cbSize = sizeof(MENUITEMINFO);
\r
439 info.fMask = MIIM_STATE | MIIM_TYPE;
\r
440 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
442 info.dwTypeData = buf;
\r
443 info.cch = sizeof(buf);
\r
444 GetMenuItemInfo(subMenu, j, TRUE, &info);
\r
446 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );
\r
447 else menuText[i][j] = strdup(buf); // remember original on first change
\r
449 if(buf[0] == NULLCHAR) continue;
\r
450 info.dwTypeData = T_(buf);
\r
451 info.cch = strlen(buf)+1;
\r
452 SetMenuItemInfo(subMenu, j, TRUE, &info);
\r
458 TranslateMenus(int addLanguage)
\r
461 WIN32_FIND_DATA fileData;
\r
463 #define IDM_English 1970
\r
465 HMENU mainMenu = GetMenu(hwndMain);
\r
466 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
467 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
468 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
469 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
470 TranslateOneMenu(i, subMenu);
\r
472 DrawMenuBar(hwndMain);
\r
475 if(!addLanguage) return;
\r
476 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
477 HMENU mainMenu = GetMenu(hwndMain);
\r
478 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
479 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
480 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
481 i = 0; lastChecked = IDM_English;
\r
483 char *p, *q = fileData.cFileName;
\r
484 int checkFlag = MF_UNCHECKED;
\r
485 languageFile[i] = strdup(q);
\r
486 if(barbaric && !strcmp(oldLanguage, q)) {
\r
487 checkFlag = MF_CHECKED;
\r
488 lastChecked = IDM_English + i + 1;
\r
489 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
491 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
492 p = strstr(fileData.cFileName, ".lng");
\r
494 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
495 } while(FindNextFile(hFind, &fileData));
\r
502 #define IDM_RecentEngines 3000
\r
505 RecentEngineMenu (char *s)
\r
507 if(appData.icsActive) return;
\r
508 if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty
\r
509 HMENU mainMenu = GetMenu(hwndMain);
\r
510 HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu
\r
511 int i=IDM_RecentEngines;
\r
512 recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu
\r
513 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
515 char *p = strchr(s, '\n');
\r
516 if(p == NULL) return; // malformed!
\r
518 AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);
\r
532 int cliWidth, cliHeight;
\r
535 SizeInfo sizeInfo[] =
\r
537 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
538 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
539 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
540 { "petite", 33, 1, 1, 1, 0, 0 },
\r
541 { "slim", 37, 2, 1, 0, 0, 0 },
\r
542 { "small", 40, 2, 1, 0, 0, 0 },
\r
543 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
544 { "middling", 49, 2, 0, 0, 0, 0 },
\r
545 { "average", 54, 2, 0, 0, 0, 0 },
\r
546 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
547 { "medium", 64, 3, 0, 0, 0, 0 },
\r
548 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
549 { "large", 80, 3, 0, 0, 0, 0 },
\r
550 { "big", 87, 3, 0, 0, 0, 0 },
\r
551 { "huge", 95, 3, 0, 0, 0, 0 },
\r
552 { "giant", 108, 3, 0, 0, 0, 0 },
\r
553 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
554 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
555 { NULL, 0, 0, 0, 0, 0, 0 }
\r
558 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
559 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
561 { 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
562 { 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
563 { 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
564 { 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
565 { 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
566 { 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
567 { 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
568 { 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
569 { 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
570 { 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
571 { 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
572 { 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
573 { 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
574 { 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
575 { 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
576 { 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
577 { 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
578 { 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
581 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
590 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
591 #define N_BUTTONS 5
\r
593 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
595 {"<<", IDM_ToStart, NULL, NULL},
\r
596 {"<", IDM_Backward, NULL, NULL},
\r
597 {"P", IDM_Pause, NULL, NULL},
\r
598 {">", IDM_Forward, NULL, NULL},
\r
599 {">>", IDM_ToEnd, NULL, NULL},
\r
602 int tinyLayout = 0, smallLayout = 0;
\r
603 #define MENU_BAR_ITEMS 9
\r
604 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
605 { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },
\r
606 { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },
\r
610 MySound sounds[(int)NSoundClasses];
\r
611 MyTextAttribs textAttribs[(int)NColorClasses];
\r
613 MyColorizeAttribs colorizeAttribs[] = {
\r
614 { (COLORREF)0, 0, N_("Shout Text") },
\r
615 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
616 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
617 { (COLORREF)0, 0, N_("Channel Text") },
\r
618 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
619 { (COLORREF)0, 0, N_("Tell Text") },
\r
620 { (COLORREF)0, 0, N_("Challenge Text") },
\r
621 { (COLORREF)0, 0, N_("Request Text") },
\r
622 { (COLORREF)0, 0, N_("Seek Text") },
\r
623 { (COLORREF)0, 0, N_("Normal Text") },
\r
624 { (COLORREF)0, 0, N_("None") }
\r
629 static char *commentTitle;
\r
630 static char *commentText;
\r
631 static int commentIndex;
\r
632 static Boolean editComment = FALSE;
\r
635 char errorTitle[MSG_SIZ];
\r
636 char errorMessage[2*MSG_SIZ];
\r
637 HWND errorDialog = NULL;
\r
638 BOOLEAN moveErrorMessageUp = FALSE;
\r
639 BOOLEAN consoleEcho = TRUE;
\r
640 CHARFORMAT consoleCF;
\r
641 COLORREF consoleBackgroundColor;
\r
643 char *programVersion;
\r
649 typedef int CPKind;
\r
658 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
661 #define INPUT_SOURCE_BUF_SIZE 4096
\r
663 typedef struct _InputSource {
\r
670 char buf[INPUT_SOURCE_BUF_SIZE];
\r
674 InputCallback func;
\r
675 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
679 InputSource *consoleInputSource;
\r
684 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
685 VOID ConsoleCreate();
\r
687 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
688 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
689 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
690 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
692 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
693 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
694 void ParseIcsTextMenu(char *icsTextMenuString);
\r
695 VOID PopUpNameDialog(char firstchar);
\r
696 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
700 int GameListOptions();
\r
702 int dummy; // [HGM] for obsolete args
\r
704 HWND hwndMain = NULL; /* root window*/
\r
705 HWND hwndConsole = NULL;
\r
706 HWND commentDialog = NULL;
\r
707 HWND moveHistoryDialog = NULL;
\r
708 HWND evalGraphDialog = NULL;
\r
709 HWND engineOutputDialog = NULL;
\r
710 HWND gameListDialog = NULL;
\r
711 HWND editTagsDialog = NULL;
\r
713 int commentUp = FALSE;
\r
715 WindowPlacement wpMain;
\r
716 WindowPlacement wpConsole;
\r
717 WindowPlacement wpComment;
\r
718 WindowPlacement wpMoveHistory;
\r
719 WindowPlacement wpEvalGraph;
\r
720 WindowPlacement wpEngineOutput;
\r
721 WindowPlacement wpGameList;
\r
722 WindowPlacement wpTags;
\r
724 VOID EngineOptionsPopup(); // [HGM] settings
\r
726 VOID GothicPopUp(char *title, VariantClass variant);
\r
728 * Setting "frozen" should disable all user input other than deleting
\r
729 * the window. We do this while engines are initializing themselves.
\r
731 static int frozen = 0;
\r
732 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
738 if (frozen) return;
\r
740 hmenu = GetMenu(hwndMain);
\r
741 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
742 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
744 DrawMenuBar(hwndMain);
\r
747 /* Undo a FreezeUI */
\r
753 if (!frozen) return;
\r
755 hmenu = GetMenu(hwndMain);
\r
756 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
757 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
759 DrawMenuBar(hwndMain);
\r
762 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
764 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
770 #define JAWS_ALT_INTERCEPT
\r
771 #define JAWS_KBUP_NAVIGATION
\r
772 #define JAWS_KBDOWN_NAVIGATION
\r
773 #define JAWS_MENU_ITEMS
\r
774 #define JAWS_SILENCE
\r
775 #define JAWS_REPLAY
\r
777 #define JAWS_COPYRIGHT
\r
778 #define JAWS_DELETE(X) X
\r
779 #define SAYMACHINEMOVE()
\r
783 /*---------------------------------------------------------------------------*\
\r
787 \*---------------------------------------------------------------------------*/
\r
790 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
791 LPSTR lpCmdLine, int nCmdShow)
\r
794 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
795 // INITCOMMONCONTROLSEX ex;
\r
799 LoadLibrary("RICHED32.DLL");
\r
800 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
802 if (!InitApplication(hInstance)) {
\r
805 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
812 // InitCommonControlsEx(&ex);
\r
813 InitCommonControls();
\r
815 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
816 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
817 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
819 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
821 while (GetMessage(&msg, /* message structure */
\r
822 NULL, /* handle of window receiving the message */
\r
823 0, /* lowest message to examine */
\r
824 0)) /* highest message to examine */
\r
827 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
828 // [HGM] navigate: switch between all windows with tab
\r
829 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
830 int i, currentElement = 0;
\r
832 // first determine what element of the chain we come from (if any)
\r
833 if(appData.icsActive) {
\r
834 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
835 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
837 if(engineOutputDialog && EngineOutputIsUp()) {
\r
838 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
839 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
841 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
842 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
844 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
845 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
846 if(msg.hwnd == e1) currentElement = 2; else
\r
847 if(msg.hwnd == e2) currentElement = 3; else
\r
848 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
849 if(msg.hwnd == mh) currentElement = 4; else
\r
850 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
851 if(msg.hwnd == hText) currentElement = 5; else
\r
852 if(msg.hwnd == hInput) currentElement = 6; else
\r
853 for (i = 0; i < N_BUTTONS; i++) {
\r
854 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
857 // determine where to go to
\r
858 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
860 currentElement = (currentElement + direction) % 7;
\r
861 switch(currentElement) {
\r
863 h = hwndMain; break; // passing this case always makes the loop exit
\r
865 h = buttonDesc[0].hwnd; break; // could be NULL
\r
867 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
870 if(!EngineOutputIsUp()) continue;
\r
873 if(!MoveHistoryIsUp()) continue;
\r
875 // case 6: // input to eval graph does not seem to get here!
\r
876 // if(!EvalGraphIsUp()) continue;
\r
877 // h = evalGraphDialog; break;
\r
879 if(!appData.icsActive) continue;
\r
883 if(!appData.icsActive) continue;
\r
889 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
890 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
893 continue; // this message now has been processed
\r
897 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
898 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
899 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
900 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
901 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
902 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
903 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
904 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
905 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
906 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
907 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
908 for(i=0; i<MAX_CHAT; i++)
\r
909 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
912 if(done) continue; // [HGM] chat: end patch
\r
913 TranslateMessage(&msg); /* Translates virtual key codes */
\r
914 DispatchMessage(&msg); /* Dispatches message to window */
\r
919 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
922 /*---------------------------------------------------------------------------*\
\r
924 * Initialization functions
\r
926 \*---------------------------------------------------------------------------*/
\r
930 { // update user logo if necessary
\r
931 static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;
\r
933 if(appData.autoLogo) {
\r
934 curName = UserName();
\r
935 if(strcmp(curName, oldUserName)) {
\r
936 GetCurrentDirectory(MSG_SIZ, dir);
\r
937 SetCurrentDirectory(installDir);
\r
938 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
939 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
940 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
941 if(userLogo == NULL)
\r
942 userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
943 SetCurrentDirectory(dir); /* return to prev directory */
\r
949 InitApplication(HINSTANCE hInstance)
\r
953 /* Fill in window class structure with parameters that describe the */
\r
956 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
957 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
958 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
959 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
960 wc.hInstance = hInstance; /* Owner of this class */
\r
961 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
962 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
963 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
964 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
965 wc.lpszClassName = szAppName; /* Name to register as */
\r
967 /* Register the window class and return success/failure code. */
\r
968 if (!RegisterClass(&wc)) return FALSE;
\r
970 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
971 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
973 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
974 wc.hInstance = hInstance;
\r
975 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
976 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
977 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
978 wc.lpszMenuName = NULL;
\r
979 wc.lpszClassName = szConsoleName;
\r
981 if (!RegisterClass(&wc)) return FALSE;
\r
986 /* Set by InitInstance, used by EnsureOnScreen */
\r
987 int screenHeight, screenWidth;
\r
988 RECT screenGeometry;
\r
991 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
993 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
994 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
995 if (*x > screenGeometry.right - 32) *x = screenGeometry.left;
\r
996 if (*y > screenGeometry.bottom - 32) *y = screenGeometry.top;
\r
997 if (*x < screenGeometry.left + minX) *x = screenGeometry.left + minX;
\r
998 if (*y < screenGeometry.top + minY) *y = screenGeometry.top + minY;
\r
1002 LoadLogo(ChessProgramState *cps, int n, Boolean ics)
\r
1004 char buf[MSG_SIZ], dir[MSG_SIZ];
\r
1005 GetCurrentDirectory(MSG_SIZ, dir);
\r
1006 SetCurrentDirectory(installDir);
\r
1007 if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {
\r
1008 cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1010 if (cps->programLogo == NULL && appData.debugMode) {
\r
1011 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );
\r
1013 } else if(appData.autoLogo) {
\r
1014 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
\r
1015 char *opponent = "";
\r
1016 if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;
\r
1017 if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;
\r
1018 sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);
\r
1019 if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {
\r
1020 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
1021 cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1024 if(appData.directory[n] && appData.directory[n][0]) {
\r
1025 SetCurrentDirectory(appData.directory[n]);
\r
1026 cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1029 SetCurrentDirectory(dir); /* return to prev directory */
\r
1035 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
1036 backTextureSquareSize = 0; // kludge to force recalculation of texturemode
\r
1038 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1039 if(liteBackTexture) DeleteObject(liteBackTexture);
\r
1040 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1041 liteBackTextureMode = appData.liteBackTextureMode;
\r
1043 if (liteBackTexture == NULL && appData.debugMode) {
\r
1044 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1048 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1049 if(darkBackTexture) DeleteObject(darkBackTexture);
\r
1050 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1051 darkBackTextureMode = appData.darkBackTextureMode;
\r
1053 if (darkBackTexture == NULL && appData.debugMode) {
\r
1054 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1059 #ifndef SM_CXVIRTUALSCREEN
\r
1060 #define SM_CXVIRTUALSCREEN 78
\r
1062 #ifndef SM_CYVIRTUALSCREEN
\r
1063 #define SM_CYVIRTUALSCREEN 79
\r
1065 #ifndef SM_XVIRTUALSCREEN
\r
1066 #define SM_XVIRTUALSCREEN 76
\r
1068 #ifndef SM_YVIRTUALSCREEN
\r
1069 #define SM_YVIRTUALSCREEN 77
\r
1075 screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
\r
1076 if( !screenHeight ) screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1077 screenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
\r
1078 if( !screenWidth ) screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1079 screenGeometry.left = GetSystemMetrics(SM_XVIRTUALSCREEN);
\r
1080 screenGeometry.top = GetSystemMetrics(SM_YVIRTUALSCREEN);
\r
1081 screenGeometry.right = screenGeometry.left + screenWidth;
\r
1082 screenGeometry.bottom = screenGeometry.top + screenHeight;
\r
1086 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
1088 HWND hwnd; /* Main window handle. */
\r
1090 WINDOWPLACEMENT wp;
\r
1093 hInst = hInstance; /* Store instance handle in our global variable */
\r
1094 programName = szAppName;
\r
1096 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
1097 *filepart = NULLCHAR;
\r
1098 SetCurrentDirectory(installDir);
\r
1100 GetCurrentDirectory(MSG_SIZ, installDir);
\r
1102 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
1104 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
1105 /* xboard, and older WinBoards, controlled the move sound with the
\r
1106 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
1107 always turn the option on (so that the backend will call us),
\r
1108 then let the user turn the sound off by setting it to silence if
\r
1109 desired. To accommodate old winboard.ini files saved by old
\r
1110 versions of WinBoard, we also turn off the sound if the option
\r
1111 was initially set to false. [HGM] taken out of InitAppData */
\r
1112 if (!appData.ringBellAfterMoves) {
\r
1113 sounds[(int)SoundMove].name = strdup("");
\r
1114 appData.ringBellAfterMoves = TRUE;
\r
1116 if (appData.debugMode) {
\r
1117 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
1118 setbuf(debugFP, NULL);
\r
1121 LoadLanguageFile(appData.language);
\r
1125 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
1126 // InitEngineUCI( installDir, &second );
\r
1128 /* Create a main window for this application instance. */
\r
1129 hwnd = CreateWindow(szAppName, szTitle,
\r
1130 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
1131 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
1132 NULL, NULL, hInstance, NULL);
\r
1135 /* If window could not be created, return "failure" */
\r
1140 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1141 LoadLogo(&first, 0, FALSE);
\r
1142 LoadLogo(&second, 1, appData.icsActive);
\r
1146 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1147 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1148 iconCurrent = iconWhite;
\r
1149 InitDrawingColors();
\r
1151 InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args
\r
1152 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1153 /* Compute window size for each board size, and use the largest
\r
1154 size that fits on this screen as the default. */
\r
1155 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1156 if (boardSize == (BoardSize)-1 &&
\r
1157 winH <= screenHeight
\r
1158 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1159 && winW <= screenWidth) {
\r
1160 boardSize = (BoardSize)ibs;
\r
1164 InitDrawingSizes(boardSize, 0);
\r
1165 RecentEngineMenu(appData.recentEngineList);
\r
1167 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1169 /* [AS] Load textures if specified */
\r
1172 mysrandom( (unsigned) time(NULL) );
\r
1174 /* [AS] Restore layout */
\r
1175 if( wpMoveHistory.visible ) {
\r
1176 MoveHistoryPopUp();
\r
1179 if( wpEvalGraph.visible ) {
\r
1183 if( wpEngineOutput.visible ) {
\r
1184 EngineOutputPopUp();
\r
1187 /* Make the window visible; update its client area; and return "success" */
\r
1188 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1189 wp.length = sizeof(WINDOWPLACEMENT);
\r
1191 wp.showCmd = nCmdShow;
\r
1192 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1193 wp.rcNormalPosition.left = wpMain.x;
\r
1194 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1195 wp.rcNormalPosition.top = wpMain.y;
\r
1196 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1197 SetWindowPlacement(hwndMain, &wp);
\r
1199 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1201 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1202 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1204 if (hwndConsole) {
\r
1206 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1207 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1209 ShowWindow(hwndConsole, nCmdShow);
\r
1210 SetActiveWindow(hwndConsole);
\r
1212 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1213 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1222 HMENU hmenu = GetMenu(hwndMain);
\r
1224 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1225 MF_BYCOMMAND|((appData.icsActive &&
\r
1226 *appData.icsCommPort != NULLCHAR) ?
\r
1227 MF_ENABLED : MF_GRAYED));
\r
1228 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1229 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1230 MF_CHECKED : MF_UNCHECKED));
\r
1233 //---------------------------------------------------------------------------------------------------------
\r
1235 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1236 #define XBOARD FALSE
\r
1238 #define OPTCHAR "/"
\r
1239 #define SEPCHAR "="
\r
1240 #define TOPLEVEL 0
\r
1244 // front-end part of option handling
\r
1247 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1249 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1250 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1253 lf->lfEscapement = 0;
\r
1254 lf->lfOrientation = 0;
\r
1255 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1256 lf->lfItalic = mfp->italic;
\r
1257 lf->lfUnderline = mfp->underline;
\r
1258 lf->lfStrikeOut = mfp->strikeout;
\r
1259 lf->lfCharSet = mfp->charset;
\r
1260 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1261 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1262 lf->lfQuality = DEFAULT_QUALITY;
\r
1263 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1264 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1268 CreateFontInMF(MyFont *mf)
\r
1270 LFfromMFP(&mf->lf, &mf->mfp);
\r
1271 if (mf->hf) DeleteObject(mf->hf);
\r
1272 mf->hf = CreateFontIndirect(&mf->lf);
\r
1275 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1277 colorVariable[] = {
\r
1278 &whitePieceColor,
\r
1279 &blackPieceColor,
\r
1280 &lightSquareColor,
\r
1281 &darkSquareColor,
\r
1282 &highlightSquareColor,
\r
1283 &premoveHighlightColor,
\r
1285 &consoleBackgroundColor,
\r
1286 &appData.fontForeColorWhite,
\r
1287 &appData.fontBackColorWhite,
\r
1288 &appData.fontForeColorBlack,
\r
1289 &appData.fontBackColorBlack,
\r
1290 &appData.evalHistColorWhite,
\r
1291 &appData.evalHistColorBlack,
\r
1292 &appData.highlightArrowColor,
\r
1295 /* Command line font name parser. NULL name means do nothing.
\r
1296 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1297 For backward compatibility, syntax without the colon is also
\r
1298 accepted, but font names with digits in them won't work in that case.
\r
1301 ParseFontName(char *name, MyFontParams *mfp)
\r
1304 if (name == NULL) return;
\r
1306 q = strchr(p, ':');
\r
1308 if (q - p >= sizeof(mfp->faceName))
\r
1309 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1310 memcpy(mfp->faceName, p, q - p);
\r
1311 mfp->faceName[q - p] = NULLCHAR;
\r
1314 q = mfp->faceName;
\r
1316 while (*p && !isdigit(*p)) {
\r
1318 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1319 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1321 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1324 if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);
\r
1325 mfp->pointSize = (float) atof(p);
\r
1326 mfp->bold = (strchr(p, 'b') != NULL);
\r
1327 mfp->italic = (strchr(p, 'i') != NULL);
\r
1328 mfp->underline = (strchr(p, 'u') != NULL);
\r
1329 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1330 mfp->charset = DEFAULT_CHARSET;
\r
1331 q = strchr(p, 'c');
\r
1333 mfp->charset = (BYTE) atoi(q+1);
\r
1337 ParseFont(char *name, int number)
\r
1338 { // wrapper to shield back-end from 'font'
\r
1339 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1344 { // in WB we have a 2D array of fonts; this initializes their description
\r
1346 /* Point font array elements to structures and
\r
1347 parse default font names */
\r
1348 for (i=0; i<NUM_FONTS; i++) {
\r
1349 for (j=0; j<NUM_SIZES; j++) {
\r
1350 font[j][i] = &fontRec[j][i];
\r
1351 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1358 { // here we create the actual fonts from the selected descriptions
\r
1360 for (i=0; i<NUM_FONTS; i++) {
\r
1361 for (j=0; j<NUM_SIZES; j++) {
\r
1362 CreateFontInMF(font[j][i]);
\r
1366 /* Color name parser.
\r
1367 X version accepts X color names, but this one
\r
1368 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1370 ParseColorName(char *name)
\r
1372 int red, green, blue, count;
\r
1373 char buf[MSG_SIZ];
\r
1375 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1377 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1378 &red, &green, &blue);
\r
1381 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1382 DisplayError(buf, 0);
\r
1383 return RGB(0, 0, 0);
\r
1385 return PALETTERGB(red, green, blue);
\r
1389 ParseColor(int n, char *name)
\r
1390 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1391 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1395 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1397 char *e = argValue;
\r
1401 if (*e == 'b') eff |= CFE_BOLD;
\r
1402 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1403 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1404 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1405 else if (*e == '#' || isdigit(*e)) break;
\r
1409 *color = ParseColorName(e);
\r
1413 ParseTextAttribs(ColorClass cc, char *s)
\r
1414 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1415 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1416 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1420 ParseBoardSize(void *addr, char *name)
\r
1421 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1422 BoardSize bs = SizeTiny;
\r
1423 while (sizeInfo[bs].name != NULL) {
\r
1424 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1425 *(BoardSize *)addr = bs;
\r
1430 ExitArgError(_("Unrecognized board size value"), name, TRUE);
\r
1435 { // [HGM] import name from appData first
\r
1438 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1439 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1440 textAttribs[cc].sound.data = NULL;
\r
1441 MyLoadSound(&textAttribs[cc].sound);
\r
1443 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1444 textAttribs[cc].sound.name = strdup("");
\r
1445 textAttribs[cc].sound.data = NULL;
\r
1447 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1448 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1449 sounds[sc].data = NULL;
\r
1450 MyLoadSound(&sounds[sc]);
\r
1455 SetCommPortDefaults()
\r
1457 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1458 dcb.DCBlength = sizeof(DCB);
\r
1459 dcb.BaudRate = 9600;
\r
1460 dcb.fBinary = TRUE;
\r
1461 dcb.fParity = FALSE;
\r
1462 dcb.fOutxCtsFlow = FALSE;
\r
1463 dcb.fOutxDsrFlow = FALSE;
\r
1464 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1465 dcb.fDsrSensitivity = FALSE;
\r
1466 dcb.fTXContinueOnXoff = TRUE;
\r
1467 dcb.fOutX = FALSE;
\r
1469 dcb.fNull = FALSE;
\r
1470 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1471 dcb.fAbortOnError = FALSE;
\r
1473 dcb.Parity = SPACEPARITY;
\r
1474 dcb.StopBits = ONESTOPBIT;
\r
1477 // [HGM] args: these three cases taken out to stay in front-end
\r
1479 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1480 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1481 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1482 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1484 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1485 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1486 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1487 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1488 ad->argName, mfp->faceName, mfp->pointSize,
\r
1489 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1490 mfp->bold ? "b" : "",
\r
1491 mfp->italic ? "i" : "",
\r
1492 mfp->underline ? "u" : "",
\r
1493 mfp->strikeout ? "s" : "",
\r
1494 (int)mfp->charset);
\r
1500 { // [HGM] copy the names from the internal WB variables to appData
\r
1503 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1504 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1505 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1506 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1510 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1511 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1512 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1513 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1514 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1515 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1516 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1517 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1518 (ta->effects) ? " " : "",
\r
1519 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1523 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1524 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1525 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1526 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1527 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1531 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1532 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1533 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1537 ParseCommPortSettings(char *s)
\r
1538 { // wrapper to keep dcb from back-end
\r
1539 ParseCommSettings(s, &dcb);
\r
1544 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1545 GetActualPlacement(hwndMain, &wpMain);
\r
1546 GetActualPlacement(hwndConsole, &wpConsole);
\r
1547 GetActualPlacement(commentDialog, &wpComment);
\r
1548 GetActualPlacement(editTagsDialog, &wpTags);
\r
1549 GetActualPlacement(gameListDialog, &wpGameList);
\r
1550 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1551 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1552 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1556 PrintCommPortSettings(FILE *f, char *name)
\r
1557 { // wrapper to shield back-end from DCB
\r
1558 PrintCommSettings(f, name, &dcb);
\r
1562 MySearchPath(char *installDir, char *name, char *fullname)
\r
1564 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1565 if(name[0]== '%') {
\r
1566 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1567 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1568 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1569 *strchr(buf, '%') = 0;
\r
1570 strcat(fullname, getenv(buf));
\r
1571 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1573 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1574 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1575 return (int) strlen(fullname);
\r
1577 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1581 MyGetFullPathName(char *name, char *fullname)
\r
1584 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1589 { // [HGM] args: allows testing if main window is realized from back-end
\r
1590 return hwndMain != NULL;
\r
1594 PopUpStartupDialog()
\r
1598 LoadLanguageFile(appData.language);
\r
1599 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1600 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1601 FreeProcInstance(lpProc);
\r
1604 /*---------------------------------------------------------------------------*\
\r
1606 * GDI board drawing routines
\r
1608 \*---------------------------------------------------------------------------*/
\r
1610 /* [AS] Draw square using background texture */
\r
1611 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1616 return; /* Should never happen! */
\r
1619 SetGraphicsMode( dst, GM_ADVANCED );
\r
1626 /* X reflection */
\r
1631 x.eDx = (FLOAT) dw + dx - 1;
\r
1634 SetWorldTransform( dst, &x );
\r
1637 /* Y reflection */
\r
1643 x.eDy = (FLOAT) dh + dy - 1;
\r
1645 SetWorldTransform( dst, &x );
\r
1653 x.eDx = (FLOAT) dx;
\r
1654 x.eDy = (FLOAT) dy;
\r
1657 SetWorldTransform( dst, &x );
\r
1661 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1669 SetWorldTransform( dst, &x );
\r
1671 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1674 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1676 PM_WP = (int) WhitePawn,
\r
1677 PM_WN = (int) WhiteKnight,
\r
1678 PM_WB = (int) WhiteBishop,
\r
1679 PM_WR = (int) WhiteRook,
\r
1680 PM_WQ = (int) WhiteQueen,
\r
1681 PM_WF = (int) WhiteFerz,
\r
1682 PM_WW = (int) WhiteWazir,
\r
1683 PM_WE = (int) WhiteAlfil,
\r
1684 PM_WM = (int) WhiteMan,
\r
1685 PM_WO = (int) WhiteCannon,
\r
1686 PM_WU = (int) WhiteUnicorn,
\r
1687 PM_WH = (int) WhiteNightrider,
\r
1688 PM_WA = (int) WhiteAngel,
\r
1689 PM_WC = (int) WhiteMarshall,
\r
1690 PM_WAB = (int) WhiteCardinal,
\r
1691 PM_WD = (int) WhiteDragon,
\r
1692 PM_WL = (int) WhiteLance,
\r
1693 PM_WS = (int) WhiteCobra,
\r
1694 PM_WV = (int) WhiteFalcon,
\r
1695 PM_WSG = (int) WhiteSilver,
\r
1696 PM_WG = (int) WhiteGrasshopper,
\r
1697 PM_WK = (int) WhiteKing,
\r
1698 PM_BP = (int) BlackPawn,
\r
1699 PM_BN = (int) BlackKnight,
\r
1700 PM_BB = (int) BlackBishop,
\r
1701 PM_BR = (int) BlackRook,
\r
1702 PM_BQ = (int) BlackQueen,
\r
1703 PM_BF = (int) BlackFerz,
\r
1704 PM_BW = (int) BlackWazir,
\r
1705 PM_BE = (int) BlackAlfil,
\r
1706 PM_BM = (int) BlackMan,
\r
1707 PM_BO = (int) BlackCannon,
\r
1708 PM_BU = (int) BlackUnicorn,
\r
1709 PM_BH = (int) BlackNightrider,
\r
1710 PM_BA = (int) BlackAngel,
\r
1711 PM_BC = (int) BlackMarshall,
\r
1712 PM_BG = (int) BlackGrasshopper,
\r
1713 PM_BAB = (int) BlackCardinal,
\r
1714 PM_BD = (int) BlackDragon,
\r
1715 PM_BL = (int) BlackLance,
\r
1716 PM_BS = (int) BlackCobra,
\r
1717 PM_BV = (int) BlackFalcon,
\r
1718 PM_BSG = (int) BlackSilver,
\r
1719 PM_BK = (int) BlackKing
\r
1722 static HFONT hPieceFont = NULL;
\r
1723 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1724 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1725 static int fontBitmapSquareSize = 0;
\r
1726 static char pieceToFontChar[(int) EmptySquare] =
\r
1727 { 'p', 'n', 'b', 'r', 'q',
\r
1728 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1729 'k', 'o', 'm', 'v', 't', 'w',
\r
1730 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1733 extern BOOL SetCharTable( char *table, const char * map );
\r
1734 /* [HGM] moved to backend.c */
\r
1736 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1739 BYTE r1 = GetRValue( color );
\r
1740 BYTE g1 = GetGValue( color );
\r
1741 BYTE b1 = GetBValue( color );
\r
1747 /* Create a uniform background first */
\r
1748 hbrush = CreateSolidBrush( color );
\r
1749 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1750 FillRect( hdc, &rc, hbrush );
\r
1751 DeleteObject( hbrush );
\r
1754 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1755 int steps = squareSize / 2;
\r
1758 for( i=0; i<steps; i++ ) {
\r
1759 BYTE r = r1 - (r1-r2) * i / steps;
\r
1760 BYTE g = g1 - (g1-g2) * i / steps;
\r
1761 BYTE b = b1 - (b1-b2) * i / steps;
\r
1763 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1764 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1765 FillRect( hdc, &rc, hbrush );
\r
1766 DeleteObject(hbrush);
\r
1769 else if( mode == 2 ) {
\r
1770 /* Diagonal gradient, good more or less for every piece */
\r
1771 POINT triangle[3];
\r
1772 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1773 HBRUSH hbrush_old;
\r
1774 int steps = squareSize;
\r
1777 triangle[0].x = squareSize - steps;
\r
1778 triangle[0].y = squareSize;
\r
1779 triangle[1].x = squareSize;
\r
1780 triangle[1].y = squareSize;
\r
1781 triangle[2].x = squareSize;
\r
1782 triangle[2].y = squareSize - steps;
\r
1784 for( i=0; i<steps; i++ ) {
\r
1785 BYTE r = r1 - (r1-r2) * i / steps;
\r
1786 BYTE g = g1 - (g1-g2) * i / steps;
\r
1787 BYTE b = b1 - (b1-b2) * i / steps;
\r
1789 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1790 hbrush_old = SelectObject( hdc, hbrush );
\r
1791 Polygon( hdc, triangle, 3 );
\r
1792 SelectObject( hdc, hbrush_old );
\r
1793 DeleteObject(hbrush);
\r
1798 SelectObject( hdc, hpen );
\r
1803 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1804 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1805 piece: follow the steps as explained below.
\r
1807 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1811 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1817 int backColor = whitePieceColor;
\r
1818 int foreColor = blackPieceColor;
\r
1820 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1821 backColor = appData.fontBackColorWhite;
\r
1822 foreColor = appData.fontForeColorWhite;
\r
1824 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1825 backColor = appData.fontBackColorBlack;
\r
1826 foreColor = appData.fontForeColorBlack;
\r
1830 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1832 hbm_old = SelectObject( hdc, hbm );
\r
1836 rc.right = squareSize;
\r
1837 rc.bottom = squareSize;
\r
1839 /* Step 1: background is now black */
\r
1840 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1842 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1844 pt.x = (squareSize - sz.cx) / 2;
\r
1845 pt.y = (squareSize - sz.cy) / 2;
\r
1847 SetBkMode( hdc, TRANSPARENT );
\r
1848 SetTextColor( hdc, chroma );
\r
1849 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1850 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1852 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1853 /* Step 3: the area outside the piece is filled with white */
\r
1854 // FloodFill( hdc, 0, 0, chroma );
\r
1855 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1856 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1857 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1858 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1859 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1861 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1862 but if the start point is not inside the piece we're lost!
\r
1863 There should be a better way to do this... if we could create a region or path
\r
1864 from the fill operation we would be fine for example.
\r
1866 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1867 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1869 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1870 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1871 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1873 SelectObject( dc2, bm2 );
\r
1874 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1875 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1876 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1877 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1878 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1881 DeleteObject( bm2 );
\r
1884 SetTextColor( hdc, 0 );
\r
1886 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1887 draw the piece again in black for safety.
\r
1889 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1891 SelectObject( hdc, hbm_old );
\r
1893 if( hPieceMask[index] != NULL ) {
\r
1894 DeleteObject( hPieceMask[index] );
\r
1897 hPieceMask[index] = hbm;
\r
1900 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1902 SelectObject( hdc, hbm );
\r
1905 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1906 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1907 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1909 SelectObject( dc1, hPieceMask[index] );
\r
1910 SelectObject( dc2, bm2 );
\r
1911 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1912 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1915 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1916 the piece background and deletes (makes transparent) the rest.
\r
1917 Thanks to that mask, we are free to paint the background with the greates
\r
1918 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1919 We use this, to make gradients and give the pieces a "roundish" look.
\r
1921 SetPieceBackground( hdc, backColor, 2 );
\r
1922 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1926 DeleteObject( bm2 );
\r
1929 SetTextColor( hdc, foreColor );
\r
1930 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1932 SelectObject( hdc, hbm_old );
\r
1934 if( hPieceFace[index] != NULL ) {
\r
1935 DeleteObject( hPieceFace[index] );
\r
1938 hPieceFace[index] = hbm;
\r
1941 static int TranslatePieceToFontPiece( int piece )
\r
1971 case BlackMarshall:
\r
1975 case BlackNightrider:
\r
1981 case BlackUnicorn:
\r
1985 case BlackGrasshopper:
\r
1997 case BlackCardinal:
\r
2004 case WhiteMarshall:
\r
2008 case WhiteNightrider:
\r
2014 case WhiteUnicorn:
\r
2018 case WhiteGrasshopper:
\r
2030 case WhiteCardinal:
\r
2039 void CreatePiecesFromFont()
\r
2042 HDC hdc_window = NULL;
\r
2048 if( fontBitmapSquareSize < 0 ) {
\r
2049 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
2053 if( !appData.useFont || appData.renderPiecesWithFont == NULL ||
\r
2054 appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
2055 fontBitmapSquareSize = -1;
\r
2059 if( fontBitmapSquareSize != squareSize ) {
\r
2060 hdc_window = GetDC( hwndMain );
\r
2061 hdc = CreateCompatibleDC( hdc_window );
\r
2063 if( hPieceFont != NULL ) {
\r
2064 DeleteObject( hPieceFont );
\r
2067 for( i=0; i<=(int)BlackKing; i++ ) {
\r
2068 hPieceMask[i] = NULL;
\r
2069 hPieceFace[i] = NULL;
\r
2075 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
2076 fontHeight = appData.fontPieceSize;
\r
2079 fontHeight = (fontHeight * squareSize) / 100;
\r
2081 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2083 lf.lfEscapement = 0;
\r
2084 lf.lfOrientation = 0;
\r
2085 lf.lfWeight = FW_NORMAL;
\r
2087 lf.lfUnderline = 0;
\r
2088 lf.lfStrikeOut = 0;
\r
2089 lf.lfCharSet = DEFAULT_CHARSET;
\r
2090 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2091 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2092 lf.lfQuality = PROOF_QUALITY;
\r
2093 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2094 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2095 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2097 hPieceFont = CreateFontIndirect( &lf );
\r
2099 if( hPieceFont == NULL ) {
\r
2100 fontBitmapSquareSize = -2;
\r
2103 /* Setup font-to-piece character table */
\r
2104 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2105 /* No (or wrong) global settings, try to detect the font */
\r
2106 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2108 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2110 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2111 /* DiagramTT* family */
\r
2112 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2114 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2115 /* Fairy symbols */
\r
2116 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2118 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2119 /* Good Companion (Some characters get warped as literal :-( */
\r
2120 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2121 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2122 SetCharTable(pieceToFontChar, s);
\r
2125 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2126 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2130 /* Create bitmaps */
\r
2131 hfont_old = SelectObject( hdc, hPieceFont );
\r
2132 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2133 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2134 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2136 SelectObject( hdc, hfont_old );
\r
2138 fontBitmapSquareSize = squareSize;
\r
2142 if( hdc != NULL ) {
\r
2146 if( hdc_window != NULL ) {
\r
2147 ReleaseDC( hwndMain, hdc_window );
\r
2152 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2154 char name[128], buf[MSG_SIZ];
\r
2156 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2157 if(appData.pieceDirectory[0]) {
\r
2159 snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);
\r
2160 res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
2161 if(res) return res;
\r
2163 if (gameInfo.event &&
\r
2164 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2165 strcmp(name, "k80s") == 0) {
\r
2166 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2168 return LoadBitmap(hinst, name);
\r
2172 /* Insert a color into the program's logical palette
\r
2173 structure. This code assumes the given color is
\r
2174 the result of the RGB or PALETTERGB macro, and it
\r
2175 knows how those macros work (which is documented).
\r
2178 InsertInPalette(COLORREF color)
\r
2180 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2182 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2183 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2184 pLogPal->palNumEntries--;
\r
2188 pe->peFlags = (char) 0;
\r
2189 pe->peRed = (char) (0xFF & color);
\r
2190 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2191 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2197 InitDrawingColors()
\r
2200 if (pLogPal == NULL) {
\r
2201 /* Allocate enough memory for a logical palette with
\r
2202 * PALETTESIZE entries and set the size and version fields
\r
2203 * of the logical palette structure.
\r
2205 pLogPal = (NPLOGPALETTE)
\r
2206 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2207 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2208 pLogPal->palVersion = 0x300;
\r
2210 pLogPal->palNumEntries = 0;
\r
2212 InsertInPalette(lightSquareColor);
\r
2213 InsertInPalette(darkSquareColor);
\r
2214 InsertInPalette(whitePieceColor);
\r
2215 InsertInPalette(blackPieceColor);
\r
2216 InsertInPalette(highlightSquareColor);
\r
2217 InsertInPalette(premoveHighlightColor);
\r
2219 /* create a logical color palette according the information
\r
2220 * in the LOGPALETTE structure.
\r
2222 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2224 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2225 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2226 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2227 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2228 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2229 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2230 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2231 for(i=0; i<8;i++) markerBrush[i] = CreateSolidBrush(markerColor[i]); // [HGM] markers
\r
2233 /* [AS] Force rendering of the font-based pieces */
\r
2234 if( fontBitmapSquareSize > 0 ) {
\r
2235 fontBitmapSquareSize = 0;
\r
2241 BoardWidth(int boardSize, int n)
\r
2242 { /* [HGM] argument n added to allow different width and height */
\r
2243 int lineGap = sizeInfo[boardSize].lineGap;
\r
2245 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2246 lineGap = appData.overrideLineGap;
\r
2249 return (n + 1) * lineGap +
\r
2250 n * sizeInfo[boardSize].squareSize;
\r
2253 /* Respond to board resize by dragging edge */
\r
2255 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2257 BoardSize newSize = NUM_SIZES - 1;
\r
2258 static int recurse = 0;
\r
2259 if (IsIconic(hwndMain)) return;
\r
2260 if (recurse > 0) return;
\r
2262 while (newSize > 0) {
\r
2263 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2264 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2265 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2268 boardSize = newSize;
\r
2269 InitDrawingSizes(boardSize, flags);
\r
2274 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2277 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2279 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2280 ChessSquare piece;
\r
2281 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2283 SIZE clockSize, messageSize;
\r
2285 char buf[MSG_SIZ];
\r
2287 HMENU hmenu = GetMenu(hwndMain);
\r
2288 RECT crect, wrect, oldRect;
\r
2290 LOGBRUSH logbrush;
\r
2291 VariantClass v = gameInfo.variant;
\r
2293 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2294 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2296 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2297 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2298 if(boardSize == -1) return; // no size defined yet; abort (to allow early call of InitPosition)
\r
2299 oldBoardSize = boardSize;
\r
2301 if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)
\r
2302 { // correct board size to one where built-in pieces exist
\r
2303 if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)
\r
2304 && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range
\r
2306 || (v == VariantShogi && boardSize != SizeModerate) // Japanese-style Shogi
\r
2307 || v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan
\r
2308 || v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy || v == VariantLion ) {
\r
2309 if(boardSize < SizeMediocre) boardSize = SizePetite; else
\r
2310 if(boardSize > SizeModerate) boardSize = SizeBulky; else
\r
2311 boardSize = SizeMiddling;
\r
2314 if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite
\r
2316 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2317 oldRect.top = wpMain.y;
\r
2318 oldRect.right = wpMain.x + wpMain.width;
\r
2319 oldRect.bottom = wpMain.y + wpMain.height;
\r
2321 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2322 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2323 squareSize = sizeInfo[boardSize].squareSize;
\r
2324 lineGap = sizeInfo[boardSize].lineGap;
\r
2325 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2326 border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;
\r
2328 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2329 lineGap = appData.overrideLineGap;
\r
2332 if (tinyLayout != oldTinyLayout) {
\r
2333 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2335 style &= ~WS_SYSMENU;
\r
2336 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2337 "&Minimize\tCtrl+F4");
\r
2339 style |= WS_SYSMENU;
\r
2340 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2342 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2344 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2345 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2346 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2348 DrawMenuBar(hwndMain);
\r
2351 boardWidth = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;
\r
2352 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;
\r
2354 /* Get text area sizes */
\r
2355 hdc = GetDC(hwndMain);
\r
2356 if (appData.clockMode) {
\r
2357 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2359 snprintf(buf, MSG_SIZ, _("White"));
\r
2361 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2362 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2363 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2364 str = _("We only care about the height here");
\r
2365 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2366 SelectObject(hdc, oldFont);
\r
2367 ReleaseDC(hwndMain, hdc);
\r
2369 /* Compute where everything goes */
\r
2370 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2371 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2372 logoHeight = 2*clockSize.cy;
\r
2373 leftLogoRect.left = OUTER_MARGIN;
\r
2374 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2375 leftLogoRect.top = OUTER_MARGIN;
\r
2376 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2378 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2379 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2380 rightLogoRect.top = OUTER_MARGIN;
\r
2381 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2384 whiteRect.left = leftLogoRect.right;
\r
2385 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2386 whiteRect.top = OUTER_MARGIN;
\r
2387 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2389 blackRect.right = rightLogoRect.left;
\r
2390 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2391 blackRect.top = whiteRect.top;
\r
2392 blackRect.bottom = whiteRect.bottom;
\r
2394 whiteRect.left = OUTER_MARGIN;
\r
2395 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2396 whiteRect.top = OUTER_MARGIN;
\r
2397 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2399 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2400 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2401 blackRect.top = whiteRect.top;
\r
2402 blackRect.bottom = whiteRect.bottom;
\r
2404 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2407 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2408 if (appData.showButtonBar) {
\r
2409 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2410 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2412 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2414 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2415 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2417 boardRect.left = OUTER_MARGIN;
\r
2418 boardRect.right = boardRect.left + boardWidth;
\r
2419 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2420 boardRect.bottom = boardRect.top + boardHeight;
\r
2422 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2423 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2424 oldTinyLayout = tinyLayout;
\r
2425 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2426 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2427 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2428 winW *= 1 + twoBoards;
\r
2429 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2430 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2431 wpMain.height = winH; // without disturbing window attachments
\r
2432 GetWindowRect(hwndMain, &wrect);
\r
2433 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2434 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2436 // [HGM] placement: let attached windows follow size change.
\r
2437 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2438 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2439 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2440 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2441 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2443 /* compensate if menu bar wrapped */
\r
2444 GetClientRect(hwndMain, &crect);
\r
2445 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2446 wpMain.height += offby;
\r
2448 case WMSZ_TOPLEFT:
\r
2449 SetWindowPos(hwndMain, NULL,
\r
2450 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2451 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2454 case WMSZ_TOPRIGHT:
\r
2456 SetWindowPos(hwndMain, NULL,
\r
2457 wrect.left, wrect.bottom - wpMain.height,
\r
2458 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2461 case WMSZ_BOTTOMLEFT:
\r
2463 SetWindowPos(hwndMain, NULL,
\r
2464 wrect.right - wpMain.width, wrect.top,
\r
2465 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2468 case WMSZ_BOTTOMRIGHT:
\r
2472 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2473 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2478 for (i = 0; i < N_BUTTONS; i++) {
\r
2479 if (buttonDesc[i].hwnd != NULL) {
\r
2480 DestroyWindow(buttonDesc[i].hwnd);
\r
2481 buttonDesc[i].hwnd = NULL;
\r
2483 if (appData.showButtonBar) {
\r
2484 buttonDesc[i].hwnd =
\r
2485 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2486 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2487 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2488 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2489 (HMENU) buttonDesc[i].id,
\r
2490 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2492 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2493 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2494 MAKELPARAM(FALSE, 0));
\r
2496 if (buttonDesc[i].id == IDM_Pause)
\r
2497 hwndPause = buttonDesc[i].hwnd;
\r
2498 buttonDesc[i].wndproc = (WNDPROC)
\r
2499 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2502 if (gridPen != NULL) DeleteObject(gridPen);
\r
2503 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2504 if (premovePen != NULL) DeleteObject(premovePen);
\r
2505 if (lineGap != 0) {
\r
2506 logbrush.lbStyle = BS_SOLID;
\r
2507 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2509 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2510 lineGap, &logbrush, 0, NULL);
\r
2511 logbrush.lbColor = highlightSquareColor;
\r
2513 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2514 lineGap, &logbrush, 0, NULL);
\r
2516 logbrush.lbColor = premoveHighlightColor;
\r
2518 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2519 lineGap, &logbrush, 0, NULL);
\r
2521 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2522 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2523 gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;
\r
2524 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2525 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;
\r
2526 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2527 BOARD_WIDTH * (squareSize + lineGap) + border;
\r
2528 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2530 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2531 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;
\r
2532 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2533 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2534 lineGap / 2 + (i * (squareSize + lineGap)) + border;
\r
2535 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2536 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;
\r
2537 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2541 /* [HGM] Licensing requirement */
\r
2543 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2546 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2548 GothicPopUp( "", VariantNormal);
\r
2551 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2553 /* Load piece bitmaps for this board size */
\r
2554 for (i=0; i<=2; i++) {
\r
2555 for (piece = WhitePawn;
\r
2556 (int) piece < (int) BlackPawn;
\r
2557 piece = (ChessSquare) ((int) piece + 1)) {
\r
2558 if (pieceBitmap[i][piece] != NULL)
\r
2559 DeleteObject(pieceBitmap[i][piece]);
\r
2563 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2564 // Orthodox Chess pieces
\r
2565 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2566 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2567 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2568 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2569 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2570 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2571 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2572 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2573 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2574 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2575 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2576 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2577 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2578 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2579 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2580 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2581 // in Shogi, Hijack the unused Queen for Lance
\r
2582 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2583 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2584 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2586 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2587 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2588 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2591 if(squareSize <= 72 && squareSize >= 33) {
\r
2592 /* A & C are available in most sizes now */
\r
2593 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2594 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2595 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2596 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2597 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2598 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2599 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2600 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2601 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2602 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2603 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2604 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2605 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2606 } else { // Smirf-like
\r
2607 if(gameInfo.variant == VariantSChess) {
\r
2608 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2609 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2610 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2612 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2613 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2614 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2617 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2618 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2619 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2620 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2621 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2622 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2623 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2624 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2625 } else { // WinBoard standard
\r
2626 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2627 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2628 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2633 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2634 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2635 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2636 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2637 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2638 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2639 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2640 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2641 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2642 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2643 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2644 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2645 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2646 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2647 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2648 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2649 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2650 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2651 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2652 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2653 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2654 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2655 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2656 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2657 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2658 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2659 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2660 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2661 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2662 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2663 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2664 pieceBitmap[0][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "s");
\r
2665 pieceBitmap[1][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "o");
\r
2666 pieceBitmap[2][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "w");
\r
2668 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2669 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2670 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2671 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2672 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2673 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2674 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2675 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2676 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2677 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2678 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2679 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2680 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2682 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2683 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2684 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2685 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2686 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2687 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2688 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2689 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2690 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2691 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2692 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2693 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2696 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2697 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2698 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2699 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2700 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2701 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2702 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2703 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2704 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2705 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2706 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2707 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2708 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2709 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2710 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2714 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2715 /* special Shogi support in this size */
\r
2716 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2717 for (piece = WhitePawn;
\r
2718 (int) piece < (int) BlackPawn;
\r
2719 piece = (ChessSquare) ((int) piece + 1)) {
\r
2720 if (pieceBitmap[i][piece] != NULL)
\r
2721 DeleteObject(pieceBitmap[i][piece]);
\r
2724 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2725 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2726 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2727 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2728 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2729 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2730 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2731 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2732 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2733 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2734 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2735 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2736 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2737 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2738 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2739 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2740 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2741 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2742 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2743 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2744 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2745 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2746 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2747 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2748 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2749 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2750 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2751 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2752 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2753 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2754 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2755 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2756 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2757 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2758 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2759 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2760 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2761 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2762 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2763 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2764 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2765 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2771 PieceBitmap(ChessSquare p, int kind)
\r
2773 if ((int) p >= (int) BlackPawn)
\r
2774 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2776 return pieceBitmap[kind][(int) p];
\r
2779 /***************************************************************/
\r
2781 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2782 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2784 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2785 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2789 SquareToPos(int row, int column, int * x, int * y)
\r
2792 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
2793 *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;
\r
2795 *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;
\r
2796 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
2801 DrawCoordsOnDC(HDC hdc)
\r
2803 static char files[] = "0123456789012345678901221098765432109876543210";
\r
2804 static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";
\r
2805 char str[2] = { NULLCHAR, NULLCHAR };
\r
2806 int oldMode, oldAlign, x, y, start, i;
\r
2810 if (!appData.showCoords)
\r
2813 start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;
\r
2815 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2816 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2817 oldAlign = GetTextAlign(hdc);
\r
2818 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2820 y = boardRect.top + lineGap;
\r
2821 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2824 SetTextAlign(hdc, TA_RIGHT|TA_TOP);
\r
2825 x += border - lineGap - 4; y += squareSize - 6;
\r
2827 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2828 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2829 str[0] = files[start + i];
\r
2830 ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);
\r
2831 y += squareSize + lineGap;
\r
2834 start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;
\r
2837 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2838 x += -border + 4; y += border - squareSize + 6;
\r
2840 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2841 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2842 str[0] = ranks[start + i];
\r
2843 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2844 x += squareSize + lineGap;
\r
2847 SelectObject(hdc, oldBrush);
\r
2848 SetBkMode(hdc, oldMode);
\r
2849 SetTextAlign(hdc, oldAlign);
\r
2850 SelectObject(hdc, oldFont);
\r
2854 DrawGridOnDC(HDC hdc)
\r
2858 if (lineGap != 0) {
\r
2859 oldPen = SelectObject(hdc, gridPen);
\r
2860 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2861 SelectObject(hdc, oldPen);
\r
2865 #define HIGHLIGHT_PEN 0
\r
2866 #define PREMOVE_PEN 1
\r
2869 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2872 HPEN oldPen, hPen;
\r
2873 if (lineGap == 0) return;
\r
2875 x1 = boardRect.left +
\r
2876 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;
\r
2877 y1 = boardRect.top +
\r
2878 lineGap/2 + y * (squareSize + lineGap) + border;
\r
2880 x1 = boardRect.left +
\r
2881 lineGap/2 + x * (squareSize + lineGap) + border;
\r
2882 y1 = boardRect.top +
\r
2883 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;
\r
2885 hPen = pen ? premovePen : highlightPen;
\r
2886 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2887 MoveToEx(hdc, x1, y1, NULL);
\r
2888 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2889 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2890 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2891 LineTo(hdc, x1, y1);
\r
2892 SelectObject(hdc, oldPen);
\r
2896 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2899 for (i=0; i<2; i++) {
\r
2900 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2901 DrawHighlightOnDC(hdc, TRUE,
\r
2902 h->sq[i].x, h->sq[i].y,
\r
2907 /* Note: sqcolor is used only in monoMode */
\r
2908 /* Note that this code is largely duplicated in woptions.c,
\r
2909 function DrawSampleSquare, so that needs to be updated too */
\r
2911 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2913 HBITMAP oldBitmap;
\r
2917 if (appData.blindfold) return;
\r
2919 /* [AS] Use font-based pieces if needed */
\r
2920 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2921 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2922 CreatePiecesFromFont();
\r
2924 if( fontBitmapSquareSize == squareSize ) {
\r
2925 int index = TranslatePieceToFontPiece(piece);
\r
2927 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2929 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2930 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2934 squareSize, squareSize,
\r
2939 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2941 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2942 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2946 squareSize, squareSize,
\r
2955 if (appData.monoMode) {
\r
2956 SelectObject(tmphdc, PieceBitmap(piece,
\r
2957 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2958 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2959 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2961 HBRUSH xBrush = whitePieceBrush;
\r
2962 tmpSize = squareSize;
\r
2963 if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);
\r
2965 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2966 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2967 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2968 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2969 x += (squareSize - minorSize)>>1;
\r
2970 y += squareSize - minorSize - 2;
\r
2971 tmpSize = minorSize;
\r
2973 if (color || appData.allWhite ) {
\r
2974 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2976 oldBrush = SelectObject(hdc, xBrush);
\r
2977 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2978 if(appData.upsideDown && color==flipView)
\r
2979 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2981 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2982 /* Use black for outline of white pieces */
\r
2983 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2984 if(appData.upsideDown && color==flipView)
\r
2985 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2987 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2988 } else if(appData.pieceDirectory[0]) {
\r
2989 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2990 oldBrush = SelectObject(hdc, xBrush);
\r
2991 if(appData.upsideDown && color==flipView)
\r
2992 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2994 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2995 SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2996 if(appData.upsideDown && color==flipView)
\r
2997 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2999 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
3001 /* Use square color for details of black pieces */
\r
3002 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
3003 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
3004 if(appData.upsideDown && !flipView)
\r
3005 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
3007 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
3009 SelectObject(hdc, oldBrush);
\r
3010 SelectObject(tmphdc, oldBitmap);
\r
3014 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
3015 int GetBackTextureMode( int algo )
\r
3017 int result = BACK_TEXTURE_MODE_DISABLED;
\r
3021 case BACK_TEXTURE_MODE_PLAIN:
\r
3022 result = 1; /* Always use identity map */
\r
3024 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
3025 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
3033 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
3034 to handle redraws cleanly (as random numbers would always be different).
\r
3036 VOID RebuildTextureSquareInfo()
\r
3046 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
3048 if( liteBackTexture != NULL ) {
\r
3049 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3050 lite_w = bi.bmWidth;
\r
3051 lite_h = bi.bmHeight;
\r
3055 if( darkBackTexture != NULL ) {
\r
3056 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3057 dark_w = bi.bmWidth;
\r
3058 dark_h = bi.bmHeight;
\r
3062 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
3063 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
3064 if( (col + row) & 1 ) {
\r
3066 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
3067 if( lite_w >= squareSize*BOARD_WIDTH )
\r
3068 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
3070 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
3071 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
3072 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
3074 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
3075 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
3080 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
3081 if( dark_w >= squareSize*BOARD_WIDTH )
\r
3082 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
3084 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
3085 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
3086 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
3088 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
3089 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
3096 /* [AS] Arrow highlighting support */
\r
3098 static double A_WIDTH = 5; /* Width of arrow body */
\r
3100 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
3101 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
3103 static double Sqr( double x )
\r
3108 static int Round( double x )
\r
3110 return (int) (x + 0.5);
\r
3113 /* Draw an arrow between two points using current settings */
\r
3114 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
3117 double dx, dy, j, k, x, y;
\r
3119 if( d_x == s_x ) {
\r
3120 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3122 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
3125 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
3126 arrow[1].y = d_y - h;
\r
3128 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3129 arrow[2].y = d_y - h;
\r
3134 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3135 arrow[5].y = d_y - h;
\r
3137 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3138 arrow[4].y = d_y - h;
\r
3140 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3143 else if( d_y == s_y ) {
\r
3144 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3147 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3149 arrow[1].x = d_x - w;
\r
3150 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3152 arrow[2].x = d_x - w;
\r
3153 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3158 arrow[5].x = d_x - w;
\r
3159 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3161 arrow[4].x = d_x - w;
\r
3162 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3165 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3168 /* [AS] Needed a lot of paper for this! :-) */
\r
3169 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3170 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3172 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3174 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3179 arrow[0].x = Round(x - j);
\r
3180 arrow[0].y = Round(y + j*dx);
\r
3182 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3183 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3186 x = (double) d_x - k;
\r
3187 y = (double) d_y - k*dy;
\r
3190 x = (double) d_x + k;
\r
3191 y = (double) d_y + k*dy;
\r
3194 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3196 arrow[6].x = Round(x - j);
\r
3197 arrow[6].y = Round(y + j*dx);
\r
3199 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3200 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3202 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3203 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3208 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3209 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3212 Polygon( hdc, arrow, 7 );
\r
3215 /* [AS] Draw an arrow between two squares */
\r
3216 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3218 int s_x, s_y, d_x, d_y;
\r
3225 if( s_col == d_col && s_row == d_row ) {
\r
3229 /* Get source and destination points */
\r
3230 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3231 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3234 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3236 else if( d_y < s_y ) {
\r
3237 d_y += squareSize / 2 + squareSize / 4;
\r
3240 d_y += squareSize / 2;
\r
3244 d_x += squareSize / 2 - squareSize / 4;
\r
3246 else if( d_x < s_x ) {
\r
3247 d_x += squareSize / 2 + squareSize / 4;
\r
3250 d_x += squareSize / 2;
\r
3253 s_x += squareSize / 2;
\r
3254 s_y += squareSize / 2;
\r
3256 /* Adjust width */
\r
3257 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3260 stLB.lbStyle = BS_SOLID;
\r
3261 stLB.lbColor = appData.highlightArrowColor;
\r
3264 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3265 holdpen = SelectObject( hdc, hpen );
\r
3266 hbrush = CreateBrushIndirect( &stLB );
\r
3267 holdbrush = SelectObject( hdc, hbrush );
\r
3269 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3271 SelectObject( hdc, holdpen );
\r
3272 SelectObject( hdc, holdbrush );
\r
3273 DeleteObject( hpen );
\r
3274 DeleteObject( hbrush );
\r
3277 BOOL HasHighlightInfo()
\r
3279 BOOL result = FALSE;
\r
3281 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3282 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3293 BOOL IsDrawArrowEnabled()
\r
3295 BOOL result = FALSE;
\r
3297 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3304 VOID DrawArrowHighlight( HDC hdc )
\r
3306 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3307 DrawArrowBetweenSquares( hdc,
\r
3308 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3309 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3313 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3315 HRGN result = NULL;
\r
3317 if( HasHighlightInfo() ) {
\r
3318 int x1, y1, x2, y2;
\r
3319 int sx, sy, dx, dy;
\r
3321 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3322 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3324 sx = MIN( x1, x2 );
\r
3325 sy = MIN( y1, y2 );
\r
3326 dx = MAX( x1, x2 ) + squareSize;
\r
3327 dy = MAX( y1, y2 ) + squareSize;
\r
3329 result = CreateRectRgn( sx, sy, dx, dy );
\r
3336 Warning: this function modifies the behavior of several other functions.
\r
3338 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3339 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3340 repaint is scattered all over the place, which is not good for features such as
\r
3341 "arrow highlighting" that require a full repaint of the board.
\r
3343 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3344 user interaction, when speed is not so important) but especially to avoid errors
\r
3345 in the displayed graphics.
\r
3347 In such patched places, I always try refer to this function so there is a single
\r
3348 place to maintain knowledge.
\r
3350 To restore the original behavior, just return FALSE unconditionally.
\r
3352 BOOL IsFullRepaintPreferrable()
\r
3354 BOOL result = FALSE;
\r
3356 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3357 /* Arrow may appear on the board */
\r
3365 This function is called by DrawPosition to know whether a full repaint must
\r
3368 Only DrawPosition may directly call this function, which makes use of
\r
3369 some state information. Other function should call DrawPosition specifying
\r
3370 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3372 BOOL DrawPositionNeedsFullRepaint()
\r
3374 BOOL result = FALSE;
\r
3377 Probably a slightly better policy would be to trigger a full repaint
\r
3378 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3379 but animation is fast enough that it's difficult to notice.
\r
3381 if( animInfo.piece == EmptySquare ) {
\r
3382 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3390 static HBITMAP borderBitmap;
\r
3393 DrawBackgroundOnDC(HDC hdc)
\r
3399 static char oldBorder[MSG_SIZ];
\r
3400 int w = 600, h = 600, mode;
\r
3402 if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid
\r
3403 strncpy(oldBorder, appData.border, MSG_SIZ-1);
\r
3404 borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
3406 if(borderBitmap == NULL) { // loading failed, use white
\r
3407 FillRect( hdc, &boardRect, whitePieceBrush );
\r
3410 tmphdc = CreateCompatibleDC(hdc);
\r
3411 hbm = SelectObject(tmphdc, borderBitmap);
\r
3412 if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {
\r
3416 mode = SetStretchBltMode(hdc, COLORONCOLOR);
\r
3417 StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left,
\r
3418 boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3419 SetStretchBltMode(hdc, mode);
\r
3420 SelectObject(tmphdc, hbm);
\r
3425 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3427 int row, column, x, y, square_color, piece_color;
\r
3428 ChessSquare piece;
\r
3430 HDC texture_hdc = NULL;
\r
3432 /* [AS] Initialize background textures if needed */
\r
3433 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3434 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3435 if( backTextureSquareSize != squareSize
\r
3436 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3437 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3438 backTextureSquareSize = squareSize;
\r
3439 RebuildTextureSquareInfo();
\r
3442 texture_hdc = CreateCompatibleDC( hdc );
\r
3445 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3446 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3448 SquareToPos(row, column, &x, &y);
\r
3450 piece = board[row][column];
\r
3452 square_color = ((column + row) % 2) == 1;
\r
3453 if( gameInfo.variant == VariantXiangqi ) {
\r
3454 square_color = !InPalace(row, column);
\r
3455 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3456 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3458 piece_color = (int) piece < (int) BlackPawn;
\r
3461 /* [HGM] holdings file: light square or black */
\r
3462 if(column == BOARD_LEFT-2) {
\r
3463 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3466 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3470 if(column == BOARD_RGHT + 1 ) {
\r
3471 if( row < gameInfo.holdingsSize )
\r
3474 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3478 if(column == BOARD_LEFT-1 ) /* left align */
\r
3479 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3480 else if( column == BOARD_RGHT) /* right align */
\r
3481 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3482 else if( piece == DarkSquare) DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3484 if (appData.monoMode) {
\r
3485 if (piece == EmptySquare) {
\r
3486 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3487 square_color ? WHITENESS : BLACKNESS);
\r
3489 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3492 else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {
\r
3493 /* [AS] Draw the square using a texture bitmap */
\r
3494 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3495 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3496 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3499 squareSize, squareSize,
\r
3502 backTextureSquareInfo[r][c].mode,
\r
3503 backTextureSquareInfo[r][c].x,
\r
3504 backTextureSquareInfo[r][c].y );
\r
3506 SelectObject( texture_hdc, hbm );
\r
3508 if (piece != EmptySquare) {
\r
3509 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3513 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3515 oldBrush = SelectObject(hdc, brush );
\r
3516 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3517 SelectObject(hdc, oldBrush);
\r
3518 if (piece != EmptySquare)
\r
3519 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3524 if( texture_hdc != NULL ) {
\r
3525 DeleteDC( texture_hdc );
\r
3529 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3530 void fputDW(FILE *f, int x)
\r
3532 fputc(x & 255, f);
\r
3533 fputc(x>>8 & 255, f);
\r
3534 fputc(x>>16 & 255, f);
\r
3535 fputc(x>>24 & 255, f);
\r
3538 #define MAX_CLIPS 200 /* more than enough */
\r
3541 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3543 // HBITMAP bufferBitmap;
\r
3548 int w = 100, h = 50;
\r
3550 if(logo == NULL) {
\r
3551 if(!logoHeight) return;
\r
3552 FillRect( hdc, &logoRect, whitePieceBrush );
\r
3554 // GetClientRect(hwndMain, &Rect);
\r
3555 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3556 // Rect.bottom-Rect.top+1);
\r
3557 tmphdc = CreateCompatibleDC(hdc);
\r
3558 hbm = SelectObject(tmphdc, logo);
\r
3559 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3563 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3564 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3565 SelectObject(tmphdc, hbm);
\r
3573 HDC hdc = GetDC(hwndMain);
\r
3574 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3575 if(appData.autoLogo) {
\r
3577 switch(gameMode) { // pick logos based on game mode
\r
3578 case IcsObserving:
\r
3579 whiteLogo = second.programLogo; // ICS logo
\r
3580 blackLogo = second.programLogo;
\r
3583 case IcsPlayingWhite:
\r
3584 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3585 blackLogo = second.programLogo; // ICS logo
\r
3587 case IcsPlayingBlack:
\r
3588 whiteLogo = second.programLogo; // ICS logo
\r
3589 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3591 case TwoMachinesPlay:
\r
3592 if(first.twoMachinesColor[0] == 'b') {
\r
3593 whiteLogo = second.programLogo;
\r
3594 blackLogo = first.programLogo;
\r
3597 case MachinePlaysWhite:
\r
3598 blackLogo = userLogo;
\r
3600 case MachinePlaysBlack:
\r
3601 whiteLogo = userLogo;
\r
3602 blackLogo = first.programLogo;
\r
3605 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3606 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3607 ReleaseDC(hwndMain, hdc);
\r
3612 UpdateLogos(int display)
\r
3613 { // called after loading new engine(s), in tourney or from menu
\r
3614 LoadLogo(&first, 0, FALSE);
\r
3615 LoadLogo(&second, 1, appData.icsActive);
\r
3616 InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos
\r
3617 if(display) DisplayLogos();
\r
3620 static HDC hdcSeek;
\r
3622 // [HGM] seekgraph
\r
3623 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3626 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3627 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3628 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3629 SelectObject( hdcSeek, hp );
\r
3632 // front-end wrapper for drawing functions to do rectangles
\r
3633 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3638 if (hdcSeek == NULL) {
\r
3639 hdcSeek = GetDC(hwndMain);
\r
3640 if (!appData.monoMode) {
\r
3641 SelectPalette(hdcSeek, hPal, FALSE);
\r
3642 RealizePalette(hdcSeek);
\r
3645 hp = SelectObject( hdcSeek, gridPen );
\r
3646 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3647 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3648 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3649 SelectObject( hdcSeek, hp );
\r
3652 // front-end wrapper for putting text in graph
\r
3653 void DrawSeekText(char *buf, int x, int y)
\r
3656 SetBkMode( hdcSeek, TRANSPARENT );
\r
3657 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3658 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3661 void DrawSeekDot(int x, int y, int color)
\r
3663 int square = color & 0x80;
\r
3664 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3665 color == 0 ? markerBrush[1] : color == 1 ? darkSquareBrush : explodeBrush);
\r
3668 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3669 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3671 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3672 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3673 SelectObject(hdcSeek, oldBrush);
\r
3676 void DrawSeekOpen()
\r
3680 void DrawSeekClose()
\r
3685 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3687 static Board lastReq[2], lastDrawn[2];
\r
3688 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3689 static int lastDrawnFlipView = 0;
\r
3690 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3691 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3694 HBITMAP bufferBitmap;
\r
3695 HBITMAP oldBitmap;
\r
3697 HRGN clips[MAX_CLIPS];
\r
3698 ChessSquare dragged_piece = EmptySquare;
\r
3699 int nr = twoBoards*partnerUp;
\r
3701 /* I'm undecided on this - this function figures out whether a full
\r
3702 * repaint is necessary on its own, so there's no real reason to have the
\r
3703 * caller tell it that. I think this can safely be set to FALSE - but
\r
3704 * if we trust the callers not to request full repaints unnessesarily, then
\r
3705 * we could skip some clipping work. In other words, only request a full
\r
3706 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3707 * gamestart and similar) --Hawk
\r
3709 Boolean fullrepaint = repaint;
\r
3711 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3713 if( DrawPositionNeedsFullRepaint() ) {
\r
3714 fullrepaint = TRUE;
\r
3717 if (board == NULL) {
\r
3718 if (!lastReqValid[nr]) {
\r
3721 board = lastReq[nr];
\r
3723 CopyBoard(lastReq[nr], board);
\r
3724 lastReqValid[nr] = 1;
\r
3727 if (doingSizing) {
\r
3731 if (IsIconic(hwndMain)) {
\r
3735 if (hdc == NULL) {
\r
3736 hdc = GetDC(hwndMain);
\r
3737 if (!appData.monoMode) {
\r
3738 SelectPalette(hdc, hPal, FALSE);
\r
3739 RealizePalette(hdc);
\r
3743 releaseDC = FALSE;
\r
3746 /* Create some work-DCs */
\r
3747 hdcmem = CreateCompatibleDC(hdc);
\r
3748 tmphdc = CreateCompatibleDC(hdc);
\r
3750 /* If dragging is in progress, we temporarely remove the piece */
\r
3751 /* [HGM] or temporarily decrease count if stacked */
\r
3752 /* !! Moved to before board compare !! */
\r
3753 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3754 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3755 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3756 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3757 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3759 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3760 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3761 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3763 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3766 /* Figure out which squares need updating by comparing the
\r
3767 * newest board with the last drawn board and checking if
\r
3768 * flipping has changed.
\r
3770 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3771 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3772 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3773 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3774 SquareToPos(row, column, &x, &y);
\r
3775 clips[num_clips++] =
\r
3776 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3780 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3781 for (i=0; i<2; i++) {
\r
3782 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3783 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3784 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3785 lastDrawnHighlight.sq[i].y >= 0) {
\r
3786 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3787 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3788 clips[num_clips++] =
\r
3789 CreateRectRgn(x - lineGap, y - lineGap,
\r
3790 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3792 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3793 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3794 clips[num_clips++] =
\r
3795 CreateRectRgn(x - lineGap, y - lineGap,
\r
3796 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3800 for (i=0; i<2; i++) {
\r
3801 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3802 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3803 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3804 lastDrawnPremove.sq[i].y >= 0) {
\r
3805 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3806 lastDrawnPremove.sq[i].x, &x, &y);
\r
3807 clips[num_clips++] =
\r
3808 CreateRectRgn(x - lineGap, y - lineGap,
\r
3809 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3811 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3812 premoveHighlightInfo.sq[i].y >= 0) {
\r
3813 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3814 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3815 clips[num_clips++] =
\r
3816 CreateRectRgn(x - lineGap, y - lineGap,
\r
3817 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3821 } else { // nr == 1
\r
3822 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3823 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3824 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3825 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3826 for (i=0; i<2; i++) {
\r
3827 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3828 partnerHighlightInfo.sq[i].y >= 0) {
\r
3829 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3830 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3831 clips[num_clips++] =
\r
3832 CreateRectRgn(x - lineGap, y - lineGap,
\r
3833 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3835 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3836 oldPartnerHighlight.sq[i].y >= 0) {
\r
3837 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3838 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3839 clips[num_clips++] =
\r
3840 CreateRectRgn(x - lineGap, y - lineGap,
\r
3841 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3846 fullrepaint = TRUE;
\r
3849 /* Create a buffer bitmap - this is the actual bitmap
\r
3850 * being written to. When all the work is done, we can
\r
3851 * copy it to the real DC (the screen). This avoids
\r
3852 * the problems with flickering.
\r
3854 GetClientRect(hwndMain, &Rect);
\r
3855 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3856 Rect.bottom-Rect.top+1);
\r
3857 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3858 if (!appData.monoMode) {
\r
3859 SelectPalette(hdcmem, hPal, FALSE);
\r
3862 /* Create clips for dragging */
\r
3863 if (!fullrepaint) {
\r
3864 if (dragInfo.from.x >= 0) {
\r
3865 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3866 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3868 if (dragInfo.start.x >= 0) {
\r
3869 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3870 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3872 if (dragInfo.pos.x >= 0) {
\r
3873 x = dragInfo.pos.x - squareSize / 2;
\r
3874 y = dragInfo.pos.y - squareSize / 2;
\r
3875 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3877 if (dragInfo.lastpos.x >= 0) {
\r
3878 x = dragInfo.lastpos.x - squareSize / 2;
\r
3879 y = dragInfo.lastpos.y - squareSize / 2;
\r
3880 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3884 /* Are we animating a move?
\r
3886 * - remove the piece from the board (temporarely)
\r
3887 * - calculate the clipping region
\r
3889 if (!fullrepaint) {
\r
3890 if (animInfo.piece != EmptySquare) {
\r
3891 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3892 x = boardRect.left + animInfo.lastpos.x;
\r
3893 y = boardRect.top + animInfo.lastpos.y;
\r
3894 x2 = boardRect.left + animInfo.pos.x;
\r
3895 y2 = boardRect.top + animInfo.pos.y;
\r
3896 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3897 /* Slight kludge. The real problem is that after AnimateMove is
\r
3898 done, the position on the screen does not match lastDrawn.
\r
3899 This currently causes trouble only on e.p. captures in
\r
3900 atomic, where the piece moves to an empty square and then
\r
3901 explodes. The old and new positions both had an empty square
\r
3902 at the destination, but animation has drawn a piece there and
\r
3903 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3904 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3908 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3909 if (num_clips == 0)
\r
3910 fullrepaint = TRUE;
\r
3912 /* Set clipping on the memory DC */
\r
3913 if (!fullrepaint) {
\r
3914 SelectClipRgn(hdcmem, clips[0]);
\r
3915 for (x = 1; x < num_clips; x++) {
\r
3916 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3917 abort(); // this should never ever happen!
\r
3921 /* Do all the drawing to the memory DC */
\r
3922 if(explodeInfo.radius) { // [HGM] atomic
\r
3924 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3925 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3926 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3927 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3928 x += squareSize/2;
\r
3929 y += squareSize/2;
\r
3930 if(!fullrepaint) {
\r
3931 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3932 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3934 DrawGridOnDC(hdcmem);
\r
3935 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3936 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3937 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3938 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3939 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3940 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3941 SelectObject(hdcmem, oldBrush);
\r
3943 if(border) DrawBackgroundOnDC(hdcmem);
\r
3944 DrawGridOnDC(hdcmem);
\r
3945 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3946 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3947 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3949 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3950 oldPartnerHighlight = partnerHighlightInfo;
\r
3952 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3954 if(nr == 0) // [HGM] dual: markers only on left board
\r
3955 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3956 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3957 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3958 HBRUSH oldBrush = SelectObject(hdcmem, markerBrush[marker[row][column]-1]);
\r
3959 SquareToPos(row, column, &x, &y);
\r
3960 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3961 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3962 SelectObject(hdcmem, oldBrush);
\r
3967 if( appData.highlightMoveWithArrow ) {
\r
3968 DrawArrowHighlight(hdcmem);
\r
3971 DrawCoordsOnDC(hdcmem);
\r
3973 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3974 /* to make sure lastDrawn contains what is actually drawn */
\r
3976 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3977 if (dragged_piece != EmptySquare) {
\r
3978 /* [HGM] or restack */
\r
3979 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3980 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3982 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3983 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3985 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3986 x = dragInfo.pos.x - squareSize / 2;
\r
3987 y = dragInfo.pos.y - squareSize / 2;
\r
3988 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
3989 ((int) dragInfo.piece < (int) BlackPawn),
\r
3990 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3993 /* Put the animated piece back into place and draw it */
\r
3994 if (animInfo.piece != EmptySquare) {
\r
3995 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3996 x = boardRect.left + animInfo.pos.x;
\r
3997 y = boardRect.top + animInfo.pos.y;
\r
3998 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3999 ((int) animInfo.piece < (int) BlackPawn),
\r
4000 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
4003 /* Release the bufferBitmap by selecting in the old bitmap
\r
4004 * and delete the memory DC
\r
4006 SelectObject(hdcmem, oldBitmap);
\r
4009 /* Set clipping on the target DC */
\r
4010 if (!fullrepaint) {
\r
4011 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
4013 GetRgnBox(clips[x], &rect);
\r
4014 DeleteObject(clips[x]);
\r
4015 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
4016 rect.right + wpMain.width/2, rect.bottom);
\r
4018 SelectClipRgn(hdc, clips[0]);
\r
4019 for (x = 1; x < num_clips; x++) {
\r
4020 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
4021 abort(); // this should never ever happen!
\r
4025 /* Copy the new bitmap onto the screen in one go.
\r
4026 * This way we avoid any flickering
\r
4028 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
4029 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
4030 boardRect.right - boardRect.left,
\r
4031 boardRect.bottom - boardRect.top,
\r
4032 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
4033 if(saveDiagFlag) {
\r
4034 BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData;
\r
4035 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
4037 GetObject(bufferBitmap, sizeof(b), &b);
\r
4038 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\r
4039 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
4040 bih.biWidth = b.bmWidth;
\r
4041 bih.biHeight = b.bmHeight;
\r
4043 bih.biBitCount = b.bmBitsPixel;
\r
4044 bih.biCompression = 0;
\r
4045 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
4046 bih.biXPelsPerMeter = 0;
\r
4047 bih.biYPelsPerMeter = 0;
\r
4048 bih.biClrUsed = 0;
\r
4049 bih.biClrImportant = 0;
\r
4050 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
4051 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
4052 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
4053 // fprintf(diagFile, "%8x\n", (int) pData);
\r
4055 wb = b.bmWidthBytes;
\r
4057 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
4058 int k = ((int*) pData)[i];
\r
4059 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4060 if(j >= 16) break;
\r
4062 if(j >= nrColors) nrColors = j+1;
\r
4064 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
4066 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
4067 for(w=0; w<(wb>>2); w+=2) {
\r
4068 int k = ((int*) pData)[(wb*i>>2) + w];
\r
4069 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4070 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
4071 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
4072 pData[p++] = m | j<<4;
\r
4074 while(p&3) pData[p++] = 0;
\r
4077 wb = ((wb+31)>>5)<<2;
\r
4079 // write BITMAPFILEHEADER
\r
4080 fprintf(diagFile, "BM");
\r
4081 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
4082 fputDW(diagFile, 0);
\r
4083 fputDW(diagFile, 0x36 + (fac?64:0));
\r
4084 // write BITMAPINFOHEADER
\r
4085 fputDW(diagFile, 40);
\r
4086 fputDW(diagFile, b.bmWidth);
\r
4087 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
4088 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
4089 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
4090 fputDW(diagFile, 0);
\r
4091 fputDW(diagFile, 0);
\r
4092 fputDW(diagFile, 0);
\r
4093 fputDW(diagFile, 0);
\r
4094 fputDW(diagFile, 0);
\r
4095 fputDW(diagFile, 0);
\r
4096 // write color table
\r
4098 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
4099 // write bitmap data
\r
4100 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
4101 fputc(pData[i], diagFile);
\r
4106 SelectObject(tmphdc, oldBitmap);
\r
4108 /* Massive cleanup */
\r
4109 for (x = 0; x < num_clips; x++)
\r
4110 DeleteObject(clips[x]);
\r
4113 DeleteObject(bufferBitmap);
\r
4116 ReleaseDC(hwndMain, hdc);
\r
4118 if (lastDrawnFlipView != flipView && nr == 0) {
\r
4120 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
4122 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
4125 /* CopyBoard(lastDrawn, board);*/
\r
4126 lastDrawnHighlight = highlightInfo;
\r
4127 lastDrawnPremove = premoveHighlightInfo;
\r
4128 lastDrawnFlipView = flipView;
\r
4129 lastDrawnValid[nr] = 1;
\r
4132 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
4137 saveDiagFlag = 1; diagFile = f;
\r
4138 HDCDrawPosition(NULL, TRUE, NULL);
\r
4146 /*---------------------------------------------------------------------------*\
\r
4147 | CLIENT PAINT PROCEDURE
\r
4148 | This is the main event-handler for the WM_PAINT message.
\r
4150 \*---------------------------------------------------------------------------*/
\r
4152 PaintProc(HWND hwnd)
\r
4158 if((hdc = BeginPaint(hwnd, &ps))) {
\r
4159 if (IsIconic(hwnd)) {
\r
4160 DrawIcon(hdc, 2, 2, iconCurrent);
\r
4162 if (!appData.monoMode) {
\r
4163 SelectPalette(hdc, hPal, FALSE);
\r
4164 RealizePalette(hdc);
\r
4166 HDCDrawPosition(hdc, 1, NULL);
\r
4167 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
4168 flipView = !flipView; partnerUp = !partnerUp;
\r
4169 HDCDrawPosition(hdc, 1, NULL);
\r
4170 flipView = !flipView; partnerUp = !partnerUp;
\r
4173 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
4174 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
4175 ETO_CLIPPED|ETO_OPAQUE,
\r
4176 &messageRect, messageText, strlen(messageText), NULL);
\r
4177 SelectObject(hdc, oldFont);
\r
4178 DisplayBothClocks();
\r
4181 EndPaint(hwnd,&ps);
\r
4189 * If the user selects on a border boundary, return -1; if off the board,
\r
4190 * return -2. Otherwise map the event coordinate to the square.
\r
4191 * The offset boardRect.left or boardRect.top must already have been
\r
4192 * subtracted from x.
\r
4194 int EventToSquare(x, limit)
\r
4199 if (x < lineGap + border)
\r
4201 x -= lineGap + border;
\r
4202 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4204 x /= (squareSize + lineGap);
\r
4216 DropEnable dropEnables[] = {
\r
4217 { 'P', DP_Pawn, N_("Pawn") },
\r
4218 { 'N', DP_Knight, N_("Knight") },
\r
4219 { 'B', DP_Bishop, N_("Bishop") },
\r
4220 { 'R', DP_Rook, N_("Rook") },
\r
4221 { 'Q', DP_Queen, N_("Queen") },
\r
4225 SetupDropMenu(HMENU hmenu)
\r
4227 int i, count, enable;
\r
4229 extern char white_holding[], black_holding[];
\r
4230 char item[MSG_SIZ];
\r
4232 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4233 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4234 dropEnables[i].piece);
\r
4236 while (p && *p++ == dropEnables[i].piece) count++;
\r
4237 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4238 enable = count > 0 || !appData.testLegality
\r
4239 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4240 && !appData.icsActive);
\r
4241 ModifyMenu(hmenu, dropEnables[i].command,
\r
4242 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4243 dropEnables[i].command, item);
\r
4247 void DragPieceBegin(int x, int y, Boolean instantly)
\r
4249 dragInfo.lastpos.x = boardRect.left + x;
\r
4250 dragInfo.lastpos.y = boardRect.top + y;
\r
4251 if(instantly) dragInfo.pos = dragInfo.lastpos;
\r
4252 dragInfo.from.x = fromX;
\r
4253 dragInfo.from.y = fromY;
\r
4254 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4255 dragInfo.start = dragInfo.from;
\r
4256 SetCapture(hwndMain);
\r
4259 void DragPieceEnd(int x, int y)
\r
4262 dragInfo.start.x = dragInfo.start.y = -1;
\r
4263 dragInfo.from = dragInfo.start;
\r
4264 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4267 void ChangeDragPiece(ChessSquare piece)
\r
4269 dragInfo.piece = piece;
\r
4272 /* Event handler for mouse messages */
\r
4274 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4278 static int recursive = 0;
\r
4280 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4283 if (message == WM_MBUTTONUP) {
\r
4284 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4285 to the middle button: we simulate pressing the left button too!
\r
4287 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4288 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4294 pt.x = LOWORD(lParam);
\r
4295 pt.y = HIWORD(lParam);
\r
4296 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4297 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4298 if (!flipView && y >= 0) {
\r
4299 y = BOARD_HEIGHT - 1 - y;
\r
4301 if (flipView && x >= 0) {
\r
4302 x = BOARD_WIDTH - 1 - x;
\r
4305 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4306 controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status
\r
4308 switch (message) {
\r
4309 case WM_LBUTTONDOWN:
\r
4310 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4311 ClockClick(flipClock); break;
\r
4312 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4313 ClockClick(!flipClock); break;
\r
4315 if(dragging) { // [HGM] lion: don't destroy dragging info if we are already dragging
\r
4316 dragInfo.start.x = dragInfo.start.y = -1;
\r
4317 dragInfo.from = dragInfo.start;
\r
4319 if(fromX == -1 && frozen) { // not sure where this is for
\r
4320 fromX = fromY = -1;
\r
4321 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4324 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4325 DrawPosition(TRUE, NULL);
\r
4328 case WM_LBUTTONUP:
\r
4329 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4330 DrawPosition(TRUE, NULL);
\r
4333 case WM_MOUSEMOVE:
\r
4334 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4335 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4336 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4337 if ((appData.animateDragging || appData.highlightDragging)
\r
4338 && (wParam & MK_LBUTTON || dragging == 2)
\r
4339 && dragInfo.from.x >= 0)
\r
4341 BOOL full_repaint = FALSE;
\r
4343 if (appData.animateDragging) {
\r
4344 dragInfo.pos = pt;
\r
4346 if (appData.highlightDragging) {
\r
4347 HoverEvent(highlightInfo.sq[1].x, highlightInfo.sq[1].y, x, y);
\r
4348 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4349 full_repaint = TRUE;
\r
4353 DrawPosition( full_repaint, NULL);
\r
4355 dragInfo.lastpos = dragInfo.pos;
\r
4359 case WM_MOUSEWHEEL: // [DM]
\r
4360 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4361 /* Mouse Wheel is being rolled forward
\r
4362 * Play moves forward
\r
4364 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4365 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4366 /* Mouse Wheel is being rolled backward
\r
4367 * Play moves backward
\r
4369 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4370 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4374 case WM_MBUTTONUP:
\r
4375 case WM_RBUTTONUP:
\r
4377 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4380 case WM_MBUTTONDOWN:
\r
4381 case WM_RBUTTONDOWN:
\r
4384 fromX = fromY = -1;
\r
4385 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4386 dragInfo.start.x = dragInfo.start.y = -1;
\r
4387 dragInfo.from = dragInfo.start;
\r
4388 dragInfo.lastpos = dragInfo.pos;
\r
4389 if (appData.highlightDragging) {
\r
4390 ClearHighlights();
\r
4393 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4394 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4395 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4396 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4397 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4401 DrawPosition(TRUE, NULL);
\r
4403 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4406 if (message == WM_MBUTTONDOWN) {
\r
4407 buttonCount = 3; /* even if system didn't think so */
\r
4408 if (wParam & MK_SHIFT)
\r
4409 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4411 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4412 } else { /* message == WM_RBUTTONDOWN */
\r
4413 /* Just have one menu, on the right button. Windows users don't
\r
4414 think to try the middle one, and sometimes other software steals
\r
4415 it, or it doesn't really exist. */
\r
4416 if(gameInfo.variant != VariantShogi)
\r
4417 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4419 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4423 SetCapture(hwndMain);
\r
4426 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4427 SetupDropMenu(hmenu);
\r
4428 MenuPopup(hwnd, pt, hmenu, -1);
\r
4438 /* Preprocess messages for buttons in main window */
\r
4440 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4442 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4445 for (i=0; i<N_BUTTONS; i++) {
\r
4446 if (buttonDesc[i].id == id) break;
\r
4448 if (i == N_BUTTONS) return 0;
\r
4449 switch (message) {
\r
4454 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4455 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4462 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4465 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4466 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4467 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4468 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4470 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4472 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4473 TypeInEvent((char)wParam);
\r
4479 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4482 static int promoStyle;
\r
4484 /* Process messages for Promotion dialog box */
\r
4486 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4490 switch (message) {
\r
4491 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4492 /* Center the dialog over the application window */
\r
4493 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4494 Translate(hDlg, DLG_PromotionKing);
\r
4495 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4496 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4497 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4498 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4499 SW_SHOW : SW_HIDE);
\r
4500 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4501 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4502 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4503 PieceToChar(WhiteAngel) != '~') ||
\r
4504 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4505 PieceToChar(BlackAngel) != '~') ) ?
\r
4506 SW_SHOW : SW_HIDE);
\r
4507 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4508 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4509 PieceToChar(WhiteMarshall) != '~') ||
\r
4510 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4511 PieceToChar(BlackMarshall) != '~') ) ?
\r
4512 SW_SHOW : SW_HIDE);
\r
4513 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4514 ShowWindow(GetDlgItem(hDlg, PB_Rook), !promoStyle ? SW_SHOW : SW_HIDE);
\r
4515 ShowWindow(GetDlgItem(hDlg, PB_Bishop), !promoStyle ? SW_SHOW : SW_HIDE);
\r
4517 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4518 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4519 SetWindowText(hDlg, "Promote?");
\r
4521 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4522 gameInfo.variant == VariantSuper ?
\r
4523 SW_SHOW : SW_HIDE);
\r
4526 case WM_COMMAND: /* message: received a command */
\r
4527 switch (LOWORD(wParam)) {
\r
4529 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4530 ClearHighlights();
\r
4531 DrawPosition(FALSE, NULL);
\r
4534 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4537 promoChar = promoStyle ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4540 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4541 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4544 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4545 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4547 case PB_Chancellor:
\r
4548 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4550 case PB_Archbishop:
\r
4551 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4554 promoChar = gameInfo.variant == VariantShogi ? '=' : promoStyle ? NULLCHAR :
\r
4555 ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight));
\r
4560 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4561 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4562 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4563 fromX = fromY = -1;
\r
4564 if (!appData.highlightLastMove) {
\r
4565 ClearHighlights();
\r
4566 DrawPosition(FALSE, NULL);
\r
4573 /* Pop up promotion dialog */
\r
4575 PromotionPopup(HWND hwnd)
\r
4579 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4580 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4581 hwnd, (DLGPROC)lpProc);
\r
4582 FreeProcInstance(lpProc);
\r
4586 PromotionPopUp(char choice)
\r
4588 promoStyle = (choice == '+');
\r
4589 DrawPosition(TRUE, NULL);
\r
4590 PromotionPopup(hwndMain);
\r
4594 LoadGameDialog(HWND hwnd, char* title)
\r
4598 char fileTitle[MSG_SIZ];
\r
4599 f = OpenFileDialog(hwnd, "rb", "",
\r
4600 appData.oldSaveStyle ? "gam" : "pgn",
\r
4602 title, &number, fileTitle, NULL);
\r
4604 cmailMsgLoaded = FALSE;
\r
4605 if (number == 0) {
\r
4606 int error = GameListBuild(f);
\r
4608 DisplayError(_("Cannot build game list"), error);
\r
4609 } else if (!ListEmpty(&gameList) &&
\r
4610 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4611 GameListPopUp(f, fileTitle);
\r
4614 GameListDestroy();
\r
4617 LoadGame(f, number, fileTitle, FALSE);
\r
4621 int get_term_width()
\r
4626 HFONT hfont, hold_font;
\r
4631 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4635 // get the text metrics
\r
4636 hdc = GetDC(hText);
\r
4637 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4638 if (consoleCF.dwEffects & CFE_BOLD)
\r
4639 lf.lfWeight = FW_BOLD;
\r
4640 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4641 lf.lfItalic = TRUE;
\r
4642 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4643 lf.lfStrikeOut = TRUE;
\r
4644 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4645 lf.lfUnderline = TRUE;
\r
4646 hfont = CreateFontIndirect(&lf);
\r
4647 hold_font = SelectObject(hdc, hfont);
\r
4648 GetTextMetrics(hdc, &tm);
\r
4649 SelectObject(hdc, hold_font);
\r
4650 DeleteObject(hfont);
\r
4651 ReleaseDC(hText, hdc);
\r
4653 // get the rectangle
\r
4654 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4656 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4659 void UpdateICSWidth(HWND hText)
\r
4661 LONG old_width, new_width;
\r
4663 new_width = get_term_width(hText, FALSE);
\r
4664 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4665 if (new_width != old_width)
\r
4667 ics_update_width(new_width);
\r
4668 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4673 ChangedConsoleFont()
\r
4676 CHARRANGE tmpsel, sel;
\r
4677 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4678 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4679 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4682 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4683 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4684 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4685 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4686 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4687 * size. This was undocumented in the version of MSVC++ that I had
\r
4688 * when I wrote the code, but is apparently documented now.
\r
4690 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4691 cfmt.bCharSet = f->lf.lfCharSet;
\r
4692 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4693 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4694 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4695 /* Why are the following seemingly needed too? */
\r
4696 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4697 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4698 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4700 tmpsel.cpMax = -1; /*999999?*/
\r
4701 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4702 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4703 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4704 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4706 paraf.cbSize = sizeof(paraf);
\r
4707 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4708 paraf.dxStartIndent = 0;
\r
4709 paraf.dxOffset = WRAP_INDENT;
\r
4710 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4711 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4712 UpdateICSWidth(hText);
\r
4715 /*---------------------------------------------------------------------------*\
\r
4717 * Window Proc for main window
\r
4719 \*---------------------------------------------------------------------------*/
\r
4721 /* Process messages for main window, etc. */
\r
4723 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4730 char fileTitle[MSG_SIZ];
\r
4731 static SnapData sd;
\r
4732 static int peek=0;
\r
4734 switch (message) {
\r
4736 case WM_PAINT: /* message: repaint portion of window */
\r
4740 case WM_ERASEBKGND:
\r
4741 if (IsIconic(hwnd)) {
\r
4742 /* Cheat; change the message */
\r
4743 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4745 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4749 case WM_LBUTTONDOWN:
\r
4750 case WM_MBUTTONDOWN:
\r
4751 case WM_RBUTTONDOWN:
\r
4752 case WM_LBUTTONUP:
\r
4753 case WM_MBUTTONUP:
\r
4754 case WM_RBUTTONUP:
\r
4755 case WM_MOUSEMOVE:
\r
4756 case WM_MOUSEWHEEL:
\r
4757 MouseEvent(hwnd, message, wParam, lParam);
\r
4761 if((char)wParam == '\b') {
\r
4762 ForwardEvent(); peek = 0;
\r
4765 JAWS_KBUP_NAVIGATION
\r
4770 if((char)wParam == '\b') {
\r
4771 if(!peek) BackwardEvent(), peek = 1;
\r
4774 JAWS_KBDOWN_NAVIGATION
\r
4780 JAWS_ALT_INTERCEPT
\r
4782 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4783 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4784 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4785 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4787 SendMessage(h, message, wParam, lParam);
\r
4788 } else if(lParam != KF_REPEAT) {
\r
4789 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4790 TypeInEvent((char)wParam);
\r
4791 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4792 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4797 case WM_PALETTECHANGED:
\r
4798 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4800 HDC hdc = GetDC(hwndMain);
\r
4801 SelectPalette(hdc, hPal, TRUE);
\r
4802 nnew = RealizePalette(hdc);
\r
4804 paletteChanged = TRUE;
\r
4806 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4808 ReleaseDC(hwnd, hdc);
\r
4812 case WM_QUERYNEWPALETTE:
\r
4813 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4815 HDC hdc = GetDC(hwndMain);
\r
4816 paletteChanged = FALSE;
\r
4817 SelectPalette(hdc, hPal, FALSE);
\r
4818 nnew = RealizePalette(hdc);
\r
4820 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4822 ReleaseDC(hwnd, hdc);
\r
4827 case WM_COMMAND: /* message: command from application menu */
\r
4828 wmId = LOWORD(wParam);
\r
4833 SAY("new game enter a move to play against the computer with white");
\r
4836 case IDM_NewGameFRC:
\r
4837 if( NewGameFRC() == 0 ) {
\r
4842 case IDM_NewVariant:
\r
4843 NewVariantPopup(hwnd);
\r
4846 case IDM_LoadGame:
\r
4847 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4850 case IDM_LoadNextGame:
\r
4854 case IDM_LoadPrevGame:
\r
4858 case IDM_ReloadGame:
\r
4862 case IDM_LoadPosition:
\r
4863 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4864 Reset(FALSE, TRUE);
\r
4867 f = OpenFileDialog(hwnd, "rb", "",
\r
4868 appData.oldSaveStyle ? "pos" : "fen",
\r
4870 _("Load Position from File"), &number, fileTitle, NULL);
\r
4872 LoadPosition(f, number, fileTitle);
\r
4876 case IDM_LoadNextPosition:
\r
4877 ReloadPosition(1);
\r
4880 case IDM_LoadPrevPosition:
\r
4881 ReloadPosition(-1);
\r
4884 case IDM_ReloadPosition:
\r
4885 ReloadPosition(0);
\r
4888 case IDM_SaveGame:
\r
4889 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4890 f = OpenFileDialog(hwnd, "a", defName,
\r
4891 appData.oldSaveStyle ? "gam" : "pgn",
\r
4893 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4895 SaveGame(f, 0, "");
\r
4899 case IDM_SavePosition:
\r
4900 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4901 f = OpenFileDialog(hwnd, "a", defName,
\r
4902 appData.oldSaveStyle ? "pos" : "fen",
\r
4904 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4906 SavePosition(f, 0, "");
\r
4910 case IDM_SaveDiagram:
\r
4911 defName = "diagram";
\r
4912 f = OpenFileDialog(hwnd, "wb", defName,
\r
4915 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
4921 case IDM_CreateBook:
\r
4922 CreateBookEvent();
\r
4925 case IDM_CopyGame:
\r
4926 CopyGameToClipboard();
\r
4929 case IDM_PasteGame:
\r
4930 PasteGameFromClipboard();
\r
4933 case IDM_CopyGameListToClipboard:
\r
4934 CopyGameListToClipboard();
\r
4937 /* [AS] Autodetect FEN or PGN data */
\r
4938 case IDM_PasteAny:
\r
4939 PasteGameOrFENFromClipboard();
\r
4942 /* [AS] Move history */
\r
4943 case IDM_ShowMoveHistory:
\r
4944 if( MoveHistoryIsUp() ) {
\r
4945 MoveHistoryPopDown();
\r
4948 MoveHistoryPopUp();
\r
4952 /* [AS] Eval graph */
\r
4953 case IDM_ShowEvalGraph:
\r
4954 if( EvalGraphIsUp() ) {
\r
4955 EvalGraphPopDown();
\r
4959 SetFocus(hwndMain);
\r
4963 /* [AS] Engine output */
\r
4964 case IDM_ShowEngineOutput:
\r
4965 if( EngineOutputIsUp() ) {
\r
4966 EngineOutputPopDown();
\r
4969 EngineOutputPopUp();
\r
4973 /* [AS] User adjudication */
\r
4974 case IDM_UserAdjudication_White:
\r
4975 UserAdjudicationEvent( +1 );
\r
4978 case IDM_UserAdjudication_Black:
\r
4979 UserAdjudicationEvent( -1 );
\r
4982 case IDM_UserAdjudication_Draw:
\r
4983 UserAdjudicationEvent( 0 );
\r
4986 /* [AS] Game list options dialog */
\r
4987 case IDM_GameListOptions:
\r
4988 GameListOptions();
\r
4995 case IDM_CopyPosition:
\r
4996 CopyFENToClipboard();
\r
4999 case IDM_PastePosition:
\r
5000 PasteFENFromClipboard();
\r
5003 case IDM_MailMove:
\r
5007 case IDM_ReloadCMailMsg:
\r
5008 Reset(TRUE, TRUE);
\r
5009 ReloadCmailMsgEvent(FALSE);
\r
5012 case IDM_Minimize:
\r
5013 ShowWindow(hwnd, SW_MINIMIZE);
\r
5020 case IDM_MachineWhite:
\r
5021 MachineWhiteEvent();
\r
5023 * refresh the tags dialog only if it's visible
\r
5025 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
5027 tags = PGNTags(&gameInfo);
\r
5028 TagsPopUp(tags, CmailMsg());
\r
5031 SAY("computer starts playing white");
\r
5034 case IDM_MachineBlack:
\r
5035 MachineBlackEvent();
\r
5037 * refresh the tags dialog only if it's visible
\r
5039 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
5041 tags = PGNTags(&gameInfo);
\r
5042 TagsPopUp(tags, CmailMsg());
\r
5045 SAY("computer starts playing black");
\r
5048 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
5049 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
5052 case IDM_TwoMachines:
\r
5053 TwoMachinesEvent();
\r
5055 * refresh the tags dialog only if it's visible
\r
5057 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
5059 tags = PGNTags(&gameInfo);
\r
5060 TagsPopUp(tags, CmailMsg());
\r
5063 SAY("computer starts playing both sides");
\r
5066 case IDM_AnalysisMode:
\r
5067 if(AnalyzeModeEvent()) {
\r
5068 SAY("analyzing current position");
\r
5072 case IDM_AnalyzeFile:
\r
5073 AnalyzeFileEvent();
\r
5076 case IDM_IcsClient:
\r
5080 case IDM_EditGame:
\r
5081 case IDM_EditGame2:
\r
5086 case IDM_EditPosition:
\r
5087 case IDM_EditPosition2:
\r
5088 EditPositionEvent();
\r
5089 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
5092 case IDM_Training:
\r
5096 case IDM_ShowGameList:
\r
5097 ShowGameListProc();
\r
5100 case IDM_EditProgs1:
\r
5101 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
5104 case IDM_LoadProg1:
\r
5105 LoadEnginePopUp(hwndMain, 0);
\r
5108 case IDM_LoadProg2:
\r
5109 LoadEnginePopUp(hwndMain, 1);
\r
5112 case IDM_EditServers:
\r
5113 EditTagsPopUp(icsNames, &icsNames);
\r
5116 case IDM_EditTags:
\r
5121 case IDM_EditBook:
\r
5125 case IDM_EditComment:
\r
5127 if (commentUp && editComment) {
\r
5130 EditCommentEvent();
\r
5151 case IDM_CallFlag:
\r
5171 case IDM_StopObserving:
\r
5172 StopObservingEvent();
\r
5175 case IDM_StopExamining:
\r
5176 StopExaminingEvent();
\r
5180 UploadGameEvent();
\r
5183 case IDM_TypeInMove:
\r
5184 TypeInEvent('\000');
\r
5187 case IDM_TypeInName:
\r
5188 PopUpNameDialog('\000');
\r
5191 case IDM_Backward:
\r
5193 SetFocus(hwndMain);
\r
5200 SetFocus(hwndMain);
\r
5205 SetFocus(hwndMain);
\r
5210 SetFocus(hwndMain);
\r
5213 case OPT_GameListNext: // [HGM] forward these two accelerators to Game List
\r
5214 case OPT_GameListPrev:
\r
5215 if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);
\r
5219 RevertEvent(FALSE);
\r
5222 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5223 RevertEvent(TRUE);
\r
5226 case IDM_TruncateGame:
\r
5227 TruncateGameEvent();
\r
5234 case IDM_RetractMove:
\r
5235 RetractMoveEvent();
\r
5238 case IDM_FlipView:
\r
5239 flipView = !flipView;
\r
5240 DrawPosition(FALSE, NULL);
\r
5243 case IDM_FlipClock:
\r
5244 flipClock = !flipClock;
\r
5245 DisplayBothClocks();
\r
5249 case IDM_MuteSounds:
\r
5250 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5251 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5252 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5255 case IDM_GeneralOptions:
\r
5256 GeneralOptionsPopup(hwnd);
\r
5257 DrawPosition(TRUE, NULL);
\r
5260 case IDM_BoardOptions:
\r
5261 BoardOptionsPopup(hwnd);
\r
5264 case IDM_ThemeOptions:
\r
5265 ThemeOptionsPopup(hwnd);
\r
5268 case IDM_EnginePlayOptions:
\r
5269 EnginePlayOptionsPopup(hwnd);
\r
5272 case IDM_Engine1Options:
\r
5273 EngineOptionsPopup(hwnd, &first);
\r
5276 case IDM_Engine2Options:
\r
5278 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5279 EngineOptionsPopup(hwnd, &second);
\r
5282 case IDM_OptionsUCI:
\r
5283 UciOptionsPopup(hwnd);
\r
5287 TourneyPopup(hwnd);
\r
5290 case IDM_IcsOptions:
\r
5291 IcsOptionsPopup(hwnd);
\r
5295 FontsOptionsPopup(hwnd);
\r
5299 SoundOptionsPopup(hwnd);
\r
5302 case IDM_CommPort:
\r
5303 CommPortOptionsPopup(hwnd);
\r
5306 case IDM_LoadOptions:
\r
5307 LoadOptionsPopup(hwnd);
\r
5310 case IDM_SaveOptions:
\r
5311 SaveOptionsPopup(hwnd);
\r
5314 case IDM_TimeControl:
\r
5315 TimeControlOptionsPopup(hwnd);
\r
5318 case IDM_SaveSettings:
\r
5319 SaveSettings(settingsFileName);
\r
5322 case IDM_SaveSettingsOnExit:
\r
5323 saveSettingsOnExit = !saveSettingsOnExit;
\r
5324 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5325 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5326 MF_CHECKED : MF_UNCHECKED));
\r
5337 case IDM_AboutGame:
\r
5342 appData.debugMode = !appData.debugMode;
\r
5343 if (appData.debugMode) {
\r
5344 char dir[MSG_SIZ];
\r
5345 GetCurrentDirectory(MSG_SIZ, dir);
\r
5346 SetCurrentDirectory(installDir);
\r
5347 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5348 SetCurrentDirectory(dir);
\r
5349 setbuf(debugFP, NULL);
\r
5356 case IDM_HELPCONTENTS:
\r
5357 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5358 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5359 MessageBox (GetFocus(),
\r
5360 _("Unable to activate help"),
\r
5361 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5365 case IDM_HELPSEARCH:
\r
5366 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5367 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5368 MessageBox (GetFocus(),
\r
5369 _("Unable to activate help"),
\r
5370 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5374 case IDM_HELPHELP:
\r
5375 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5376 MessageBox (GetFocus(),
\r
5377 _("Unable to activate help"),
\r
5378 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5383 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5385 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5386 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5387 FreeProcInstance(lpProc);
\r
5390 case IDM_DirectCommand1:
\r
5391 AskQuestionEvent(_("Direct Command"),
\r
5392 _("Send to chess program:"), "", "1");
\r
5394 case IDM_DirectCommand2:
\r
5395 AskQuestionEvent(_("Direct Command"),
\r
5396 _("Send to second chess program:"), "", "2");
\r
5399 case EP_WhitePawn:
\r
5400 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5401 fromX = fromY = -1;
\r
5404 case EP_WhiteKnight:
\r
5405 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5406 fromX = fromY = -1;
\r
5409 case EP_WhiteBishop:
\r
5410 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5411 fromX = fromY = -1;
\r
5414 case EP_WhiteRook:
\r
5415 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5416 fromX = fromY = -1;
\r
5419 case EP_WhiteQueen:
\r
5420 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5421 fromX = fromY = -1;
\r
5424 case EP_WhiteFerz:
\r
5425 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5426 fromX = fromY = -1;
\r
5429 case EP_WhiteWazir:
\r
5430 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5431 fromX = fromY = -1;
\r
5434 case EP_WhiteAlfil:
\r
5435 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5436 fromX = fromY = -1;
\r
5439 case EP_WhiteCannon:
\r
5440 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5441 fromX = fromY = -1;
\r
5444 case EP_WhiteCardinal:
\r
5445 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5446 fromX = fromY = -1;
\r
5449 case EP_WhiteMarshall:
\r
5450 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5451 fromX = fromY = -1;
\r
5454 case EP_WhiteKing:
\r
5455 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5456 fromX = fromY = -1;
\r
5459 case EP_BlackPawn:
\r
5460 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5461 fromX = fromY = -1;
\r
5464 case EP_BlackKnight:
\r
5465 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5466 fromX = fromY = -1;
\r
5469 case EP_BlackBishop:
\r
5470 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5471 fromX = fromY = -1;
\r
5474 case EP_BlackRook:
\r
5475 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5476 fromX = fromY = -1;
\r
5479 case EP_BlackQueen:
\r
5480 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5481 fromX = fromY = -1;
\r
5484 case EP_BlackFerz:
\r
5485 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5486 fromX = fromY = -1;
\r
5489 case EP_BlackWazir:
\r
5490 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5491 fromX = fromY = -1;
\r
5494 case EP_BlackAlfil:
\r
5495 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5496 fromX = fromY = -1;
\r
5499 case EP_BlackCannon:
\r
5500 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5501 fromX = fromY = -1;
\r
5504 case EP_BlackCardinal:
\r
5505 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5506 fromX = fromY = -1;
\r
5509 case EP_BlackMarshall:
\r
5510 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5511 fromX = fromY = -1;
\r
5514 case EP_BlackKing:
\r
5515 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5516 fromX = fromY = -1;
\r
5519 case EP_EmptySquare:
\r
5520 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5521 fromX = fromY = -1;
\r
5524 case EP_ClearBoard:
\r
5525 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5526 fromX = fromY = -1;
\r
5530 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5531 fromX = fromY = -1;
\r
5535 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5536 fromX = fromY = -1;
\r
5540 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5541 fromX = fromY = -1;
\r
5545 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5546 fromX = fromY = -1;
\r
5550 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5551 fromX = fromY = -1;
\r
5555 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5556 fromX = fromY = -1;
\r
5560 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5561 fromX = fromY = -1;
\r
5565 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5566 fromX = fromY = -1;
\r
5570 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5571 fromX = fromY = -1;
\r
5575 barbaric = 0; appData.language = "";
\r
5576 TranslateMenus(0);
\r
5577 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5578 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5579 lastChecked = wmId;
\r
5583 if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)
\r
5584 RecentEngineEvent(wmId - IDM_RecentEngines);
\r
5586 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5587 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5588 TranslateMenus(0);
\r
5589 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5590 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5591 lastChecked = wmId;
\r
5594 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5600 case CLOCK_TIMER_ID:
\r
5601 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5602 clockTimerEvent = 0;
\r
5603 DecrementClocks(); /* call into back end */
\r
5605 case LOAD_GAME_TIMER_ID:
\r
5606 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5607 loadGameTimerEvent = 0;
\r
5608 AutoPlayGameLoop(); /* call into back end */
\r
5610 case ANALYSIS_TIMER_ID:
\r
5611 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5612 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5613 AnalysisPeriodicEvent(0);
\r
5615 KillTimer(hwnd, analysisTimerEvent);
\r
5616 analysisTimerEvent = 0;
\r
5619 case DELAYED_TIMER_ID:
\r
5620 KillTimer(hwnd, delayedTimerEvent);
\r
5621 delayedTimerEvent = 0;
\r
5622 delayedTimerCallback();
\r
5627 case WM_USER_Input:
\r
5628 InputEvent(hwnd, message, wParam, lParam);
\r
5631 /* [AS] Also move "attached" child windows */
\r
5632 case WM_WINDOWPOSCHANGING:
\r
5634 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5635 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5637 if( ((lpwp->flags & SWP_NOMOVE) == 0) /*&& ((lpwp->flags & SWP_NOSIZE) != 0)*/ ) { // [HGM] in Win8 size always accompanies move?
\r
5638 /* Window is moving */
\r
5641 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5642 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5643 rcMain.right = wpMain.x + wpMain.width;
\r
5644 rcMain.top = wpMain.y;
\r
5645 rcMain.bottom = wpMain.y + wpMain.height;
\r
5647 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5648 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5649 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5650 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5651 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5652 wpMain.x = lpwp->x;
\r
5653 wpMain.y = lpwp->y;
\r
5658 /* [AS] Snapping */
\r
5659 case WM_ENTERSIZEMOVE:
\r
5660 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5661 if (hwnd == hwndMain) {
\r
5662 doingSizing = TRUE;
\r
5665 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5669 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5670 if (hwnd == hwndMain) {
\r
5671 lastSizing = wParam;
\r
5676 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5677 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5679 case WM_EXITSIZEMOVE:
\r
5680 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5681 if (hwnd == hwndMain) {
\r
5683 doingSizing = FALSE;
\r
5684 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5685 GetClientRect(hwnd, &client);
\r
5686 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5688 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5690 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5693 case WM_DESTROY: /* message: window being destroyed */
\r
5694 PostQuitMessage(0);
\r
5698 if (hwnd == hwndMain) {
\r
5703 default: /* Passes it on if unprocessed */
\r
5704 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5709 /*---------------------------------------------------------------------------*\
\r
5711 * Misc utility routines
\r
5713 \*---------------------------------------------------------------------------*/
\r
5716 * Decent random number generator, at least not as bad as Windows
\r
5717 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5719 unsigned int randstate;
\r
5724 randstate = randstate * 1664525 + 1013904223;
\r
5725 return (int) randstate & 0x7fffffff;
\r
5729 mysrandom(unsigned int seed)
\r
5736 * returns TRUE if user selects a different color, FALSE otherwise
\r
5740 ChangeColor(HWND hwnd, COLORREF *which)
\r
5742 static BOOL firstTime = TRUE;
\r
5743 static DWORD customColors[16];
\r
5745 COLORREF newcolor;
\r
5750 /* Make initial colors in use available as custom colors */
\r
5751 /* Should we put the compiled-in defaults here instead? */
\r
5753 customColors[i++] = lightSquareColor & 0xffffff;
\r
5754 customColors[i++] = darkSquareColor & 0xffffff;
\r
5755 customColors[i++] = whitePieceColor & 0xffffff;
\r
5756 customColors[i++] = blackPieceColor & 0xffffff;
\r
5757 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5758 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5760 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5761 customColors[i++] = textAttribs[ccl].color;
\r
5763 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5764 firstTime = FALSE;
\r
5767 cc.lStructSize = sizeof(cc);
\r
5768 cc.hwndOwner = hwnd;
\r
5769 cc.hInstance = NULL;
\r
5770 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5771 cc.lpCustColors = (LPDWORD) customColors;
\r
5772 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5774 if (!ChooseColor(&cc)) return FALSE;
\r
5776 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5777 if (newcolor == *which) return FALSE;
\r
5778 *which = newcolor;
\r
5782 InitDrawingColors();
\r
5783 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5788 MyLoadSound(MySound *ms)
\r
5794 if (ms->data && ms->flag) free(ms->data);
\r
5797 switch (ms->name[0]) {
\r
5803 /* System sound from Control Panel. Don't preload here. */
\r
5807 if (ms->name[1] == NULLCHAR) {
\r
5808 /* "!" alone = silence */
\r
5811 /* Builtin wave resource. Error if not found. */
\r
5812 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5813 if (h == NULL) break;
\r
5814 ms->data = (void *)LoadResource(hInst, h);
\r
5815 ms->flag = 0; // not maloced, so cannot be freed!
\r
5816 if (h == NULL) break;
\r
5821 /* .wav file. Error if not found. */
\r
5822 f = fopen(ms->name, "rb");
\r
5823 if (f == NULL) break;
\r
5824 if (fstat(fileno(f), &st) < 0) break;
\r
5825 ms->data = malloc(st.st_size);
\r
5827 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5833 char buf[MSG_SIZ];
\r
5834 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5835 DisplayError(buf, GetLastError());
\r
5841 MyPlaySound(MySound *ms)
\r
5843 BOOLEAN ok = FALSE;
\r
5845 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5846 switch (ms->name[0]) {
\r
5848 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5853 /* System sound from Control Panel (deprecated feature).
\r
5854 "$" alone or an unset sound name gets default beep (still in use). */
\r
5855 if (ms->name[1]) {
\r
5856 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5858 if (!ok) ok = MessageBeep(MB_OK);
\r
5861 /* Builtin wave resource, or "!" alone for silence */
\r
5862 if (ms->name[1]) {
\r
5863 if (ms->data == NULL) return FALSE;
\r
5864 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5870 /* .wav file. Error if not found. */
\r
5871 if (ms->data == NULL) return FALSE;
\r
5872 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5875 /* Don't print an error: this can happen innocently if the sound driver
\r
5876 is busy; for instance, if another instance of WinBoard is playing
\r
5877 a sound at about the same time. */
\r
5883 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5886 OPENFILENAME *ofn;
\r
5887 static UINT *number; /* gross that this is static */
\r
5889 switch (message) {
\r
5890 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5891 /* Center the dialog over the application window */
\r
5892 ofn = (OPENFILENAME *) lParam;
\r
5893 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5894 number = (UINT *) ofn->lCustData;
\r
5895 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5899 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5900 Translate(hDlg, 1536);
\r
5901 return FALSE; /* Allow for further processing */
\r
5904 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5905 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5907 return FALSE; /* Allow for further processing */
\r
5913 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5915 static UINT *number;
\r
5916 OPENFILENAME *ofname;
\r
5919 case WM_INITDIALOG:
\r
5920 Translate(hdlg, DLG_IndexNumber);
\r
5921 ofname = (OPENFILENAME *)lParam;
\r
5922 number = (UINT *)(ofname->lCustData);
\r
5925 ofnot = (OFNOTIFY *)lParam;
\r
5926 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5927 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5936 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5937 char *nameFilt, char *dlgTitle, UINT *number,
\r
5938 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5940 OPENFILENAME openFileName;
\r
5941 char buf1[MSG_SIZ];
\r
5944 if (fileName == NULL) fileName = buf1;
\r
5945 if (defName == NULL) {
\r
5946 safeStrCpy(fileName, "*.", 3 );
\r
5947 strcat(fileName, defExt);
\r
5949 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5951 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5952 if (number) *number = 0;
\r
5954 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5955 openFileName.hwndOwner = hwnd;
\r
5956 openFileName.hInstance = (HANDLE) hInst;
\r
5957 openFileName.lpstrFilter = nameFilt;
\r
5958 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5959 openFileName.nMaxCustFilter = 0L;
\r
5960 openFileName.nFilterIndex = 1L;
\r
5961 openFileName.lpstrFile = fileName;
\r
5962 openFileName.nMaxFile = MSG_SIZ;
\r
5963 openFileName.lpstrFileTitle = fileTitle;
\r
5964 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5965 openFileName.lpstrInitialDir = NULL;
\r
5966 openFileName.lpstrTitle = dlgTitle;
\r
5967 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5968 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5969 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5970 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5971 openFileName.nFileOffset = 0;
\r
5972 openFileName.nFileExtension = 0;
\r
5973 openFileName.lpstrDefExt = defExt;
\r
5974 openFileName.lCustData = (LONG) number;
\r
5975 openFileName.lpfnHook = oldDialog ?
\r
5976 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5977 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5979 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5980 GetOpenFileName(&openFileName)) {
\r
5981 /* open the file */
\r
5982 f = fopen(openFileName.lpstrFile, write);
\r
5984 MessageBox(hwnd, _("File open failed"), NULL,
\r
5985 MB_OK|MB_ICONEXCLAMATION);
\r
5989 int err = CommDlgExtendedError();
\r
5990 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5999 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
6001 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
6004 * Get the first pop-up menu in the menu template. This is the
\r
6005 * menu that TrackPopupMenu displays.
\r
6007 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
6008 TranslateOneMenu(10, hmenuTrackPopup);
\r
6010 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
6013 * TrackPopup uses screen coordinates, so convert the
\r
6014 * coordinates of the mouse click to screen coordinates.
\r
6016 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
6018 /* Draw and track the floating pop-up menu. */
\r
6019 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
6020 pt.x, pt.y, 0, hwnd, NULL);
\r
6022 /* Destroy the menu.*/
\r
6023 DestroyMenu(hmenu);
\r
6028 int sizeX, sizeY, newSizeX, newSizeY;
\r
6030 } ResizeEditPlusButtonsClosure;
\r
6033 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
6035 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
6039 if (hChild == cl->hText) return TRUE;
\r
6040 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
6041 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
6042 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
6043 ScreenToClient(cl->hDlg, &pt);
\r
6044 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
6045 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
6049 /* Resize a dialog that has a (rich) edit field filling most of
\r
6050 the top, with a row of buttons below */
\r
6052 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
6055 int newTextHeight, newTextWidth;
\r
6056 ResizeEditPlusButtonsClosure cl;
\r
6058 /*if (IsIconic(hDlg)) return;*/
\r
6059 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
6061 cl.hdwp = BeginDeferWindowPos(8);
\r
6063 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
6064 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6065 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6066 if (newTextHeight < 0) {
\r
6067 newSizeY += -newTextHeight;
\r
6068 newTextHeight = 0;
\r
6070 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
6071 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6077 cl.newSizeX = newSizeX;
\r
6078 cl.newSizeY = newSizeY;
\r
6079 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
6081 EndDeferWindowPos(cl.hdwp);
\r
6084 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
6086 RECT rChild, rParent;
\r
6087 int wChild, hChild, wParent, hParent;
\r
6088 int wScreen, hScreen, xNew, yNew;
\r
6091 /* Get the Height and Width of the child window */
\r
6092 GetWindowRect (hwndChild, &rChild);
\r
6093 wChild = rChild.right - rChild.left;
\r
6094 hChild = rChild.bottom - rChild.top;
\r
6096 /* Get the Height and Width of the parent window */
\r
6097 GetWindowRect (hwndParent, &rParent);
\r
6098 wParent = rParent.right - rParent.left;
\r
6099 hParent = rParent.bottom - rParent.top;
\r
6101 /* Get the display limits */
\r
6102 hdc = GetDC (hwndChild);
\r
6103 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
6104 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
6105 ReleaseDC(hwndChild, hdc);
\r
6107 /* Calculate new X position, then adjust for screen */
\r
6108 xNew = rParent.left + ((wParent - wChild) /2);
\r
6111 } else if ((xNew+wChild) > wScreen) {
\r
6112 xNew = wScreen - wChild;
\r
6115 /* Calculate new Y position, then adjust for screen */
\r
6117 yNew = rParent.top + ((hParent - hChild) /2);
\r
6120 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
6125 } else if ((yNew+hChild) > hScreen) {
\r
6126 yNew = hScreen - hChild;
\r
6129 /* Set it, and return */
\r
6130 return SetWindowPos (hwndChild, NULL,
\r
6131 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
6134 /* Center one window over another */
\r
6135 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
6137 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
6140 /*---------------------------------------------------------------------------*\
\r
6142 * Startup Dialog functions
\r
6144 \*---------------------------------------------------------------------------*/
\r
6146 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
6148 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6150 while (*cd != NULL) {
\r
6151 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
6157 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
6159 char buf1[MAX_ARG_LEN];
\r
6162 if (str[0] == '@') {
\r
6163 FILE* f = fopen(str + 1, "r");
\r
6165 DisplayFatalError(str + 1, errno, 2);
\r
6168 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
6170 buf1[len] = NULLCHAR;
\r
6174 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6177 char buf[MSG_SIZ];
\r
6178 char *end = strchr(str, '\n');
\r
6179 if (end == NULL) return;
\r
6180 memcpy(buf, str, end - str);
\r
6181 buf[end - str] = NULLCHAR;
\r
6182 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6188 SetStartupDialogEnables(HWND hDlg)
\r
6190 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6191 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6192 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6193 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6194 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6195 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6196 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6197 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6198 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6199 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6200 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6201 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6202 IsDlgButtonChecked(hDlg, OPT_View));
\r
6206 QuoteForFilename(char *filename)
\r
6208 int dquote, space;
\r
6209 dquote = strchr(filename, '"') != NULL;
\r
6210 space = strchr(filename, ' ') != NULL;
\r
6211 if (dquote || space) {
\r
6223 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6225 char buf[MSG_SIZ];
\r
6228 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6229 q = QuoteForFilename(nthcp);
\r
6230 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6231 if (*nthdir != NULLCHAR) {
\r
6232 q = QuoteForFilename(nthdir);
\r
6233 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6235 if (*nthcp == NULLCHAR) {
\r
6236 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6237 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6238 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6239 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6244 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6246 char buf[MSG_SIZ];
\r
6250 switch (message) {
\r
6251 case WM_INITDIALOG:
\r
6252 /* Center the dialog */
\r
6253 CenterWindow (hDlg, GetDesktopWindow());
\r
6254 Translate(hDlg, DLG_Startup);
\r
6255 /* Initialize the dialog items */
\r
6256 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6257 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6258 firstChessProgramNames);
\r
6259 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6260 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6261 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6262 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6263 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6264 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6265 if (*appData.icsHelper != NULLCHAR) {
\r
6266 char *q = QuoteForFilename(appData.icsHelper);
\r
6267 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6269 if (*appData.icsHost == NULLCHAR) {
\r
6270 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6271 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6272 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6273 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6274 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6277 if (appData.icsActive) {
\r
6278 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6280 else if (appData.noChessProgram) {
\r
6281 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6284 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6287 SetStartupDialogEnables(hDlg);
\r
6291 switch (LOWORD(wParam)) {
\r
6293 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6294 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6295 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6297 comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox
\r
6298 ParseArgs(StringGet, &p);
\r
6299 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6300 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6302 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6303 ParseArgs(StringGet, &p);
\r
6304 SwapEngines(singleList); // ... and then make it 'second'
\r
6306 appData.noChessProgram = FALSE;
\r
6307 appData.icsActive = FALSE;
\r
6308 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6309 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6310 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6312 ParseArgs(StringGet, &p);
\r
6313 if (appData.zippyPlay) {
\r
6314 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6315 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6317 ParseArgs(StringGet, &p);
\r
6319 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6320 appData.noChessProgram = TRUE;
\r
6321 appData.icsActive = FALSE;
\r
6323 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6324 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6327 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6328 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6330 ParseArgs(StringGet, &p);
\r
6332 EndDialog(hDlg, TRUE);
\r
6339 case IDM_HELPCONTENTS:
\r
6340 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6341 MessageBox (GetFocus(),
\r
6342 _("Unable to activate help"),
\r
6343 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6348 SetStartupDialogEnables(hDlg);
\r
6356 /*---------------------------------------------------------------------------*\
\r
6358 * About box dialog functions
\r
6360 \*---------------------------------------------------------------------------*/
\r
6362 /* Process messages for "About" dialog box */
\r
6364 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6366 switch (message) {
\r
6367 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6368 /* Center the dialog over the application window */
\r
6369 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6370 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6371 Translate(hDlg, ABOUTBOX);
\r
6375 case WM_COMMAND: /* message: received a command */
\r
6376 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6377 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6378 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6386 /*---------------------------------------------------------------------------*\
\r
6388 * Comment Dialog functions
\r
6390 \*---------------------------------------------------------------------------*/
\r
6393 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6395 static HANDLE hwndText = NULL;
\r
6396 int len, newSizeX, newSizeY;
\r
6397 static int sizeX, sizeY;
\r
6402 switch (message) {
\r
6403 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6404 /* Initialize the dialog items */
\r
6405 Translate(hDlg, DLG_EditComment);
\r
6406 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6407 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6408 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6409 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6410 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6411 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6412 SetWindowText(hDlg, commentTitle);
\r
6413 if (editComment) {
\r
6414 SetFocus(hwndText);
\r
6416 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6418 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6419 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6420 MAKELPARAM(FALSE, 0));
\r
6421 /* Size and position the dialog */
\r
6422 if (!commentDialog) {
\r
6423 commentDialog = hDlg;
\r
6424 GetClientRect(hDlg, &rect);
\r
6425 sizeX = rect.right;
\r
6426 sizeY = rect.bottom;
\r
6427 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6428 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6429 WINDOWPLACEMENT wp;
\r
6430 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6431 wp.length = sizeof(WINDOWPLACEMENT);
\r
6433 wp.showCmd = SW_SHOW;
\r
6434 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6435 wp.rcNormalPosition.left = wpComment.x;
\r
6436 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6437 wp.rcNormalPosition.top = wpComment.y;
\r
6438 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6439 SetWindowPlacement(hDlg, &wp);
\r
6441 GetClientRect(hDlg, &rect);
\r
6442 newSizeX = rect.right;
\r
6443 newSizeY = rect.bottom;
\r
6444 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6445 newSizeX, newSizeY);
\r
6450 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6453 case WM_COMMAND: /* message: received a command */
\r
6454 switch (LOWORD(wParam)) {
\r
6456 if (editComment) {
\r
6458 /* Read changed options from the dialog box */
\r
6459 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6460 len = GetWindowTextLength(hwndText);
\r
6461 str = (char *) malloc(len + 1);
\r
6462 GetWindowText(hwndText, str, len + 1);
\r
6471 ReplaceComment(commentIndex, str);
\r
6478 case OPT_CancelComment:
\r
6482 case OPT_ClearComment:
\r
6483 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6486 case OPT_EditComment:
\r
6487 EditCommentEvent();
\r
6495 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6496 if( wParam == OPT_CommentText ) {
\r
6497 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6499 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6500 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6504 pt.x = LOWORD( lpMF->lParam );
\r
6505 pt.y = HIWORD( lpMF->lParam );
\r
6507 if(lpMF->msg == WM_CHAR) {
\r
6509 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6510 index = sel.cpMin;
\r
6512 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6514 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6515 len = GetWindowTextLength(hwndText);
\r
6516 str = (char *) malloc(len + 1);
\r
6517 GetWindowText(hwndText, str, len + 1);
\r
6518 ReplaceComment(commentIndex, str);
\r
6519 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6520 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6523 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6524 lpMF->msg = WM_USER;
\r
6532 newSizeX = LOWORD(lParam);
\r
6533 newSizeY = HIWORD(lParam);
\r
6534 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6539 case WM_GETMINMAXINFO:
\r
6540 /* Prevent resizing window too small */
\r
6541 mmi = (MINMAXINFO *) lParam;
\r
6542 mmi->ptMinTrackSize.x = 100;
\r
6543 mmi->ptMinTrackSize.y = 100;
\r
6550 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6555 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6557 if (str == NULL) str = "";
\r
6558 p = (char *) malloc(2 * strlen(str) + 2);
\r
6561 if (*str == '\n') *q++ = '\r';
\r
6565 if (commentText != NULL) free(commentText);
\r
6567 commentIndex = index;
\r
6568 commentTitle = title;
\r
6570 editComment = edit;
\r
6572 if (commentDialog) {
\r
6573 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6574 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6576 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6577 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6578 hwndMain, (DLGPROC)lpProc);
\r
6579 FreeProcInstance(lpProc);
\r
6585 /*---------------------------------------------------------------------------*\
\r
6587 * Type-in move dialog functions
\r
6589 \*---------------------------------------------------------------------------*/
\r
6592 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6594 char move[MSG_SIZ];
\r
6597 switch (message) {
\r
6598 case WM_INITDIALOG:
\r
6599 move[0] = (char) lParam;
\r
6600 move[1] = NULLCHAR;
\r
6601 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6602 Translate(hDlg, DLG_TypeInMove);
\r
6603 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6604 SetWindowText(hInput, move);
\r
6606 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6610 switch (LOWORD(wParam)) {
\r
6613 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6614 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6615 TypeInDoneEvent(move);
\r
6616 EndDialog(hDlg, TRUE);
\r
6619 EndDialog(hDlg, FALSE);
\r
6630 PopUpMoveDialog(char firstchar)
\r
6634 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6635 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6636 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6637 FreeProcInstance(lpProc);
\r
6640 /*---------------------------------------------------------------------------*\
\r
6642 * Type-in name dialog functions
\r
6644 \*---------------------------------------------------------------------------*/
\r
6647 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6649 char move[MSG_SIZ];
\r
6652 switch (message) {
\r
6653 case WM_INITDIALOG:
\r
6654 move[0] = (char) lParam;
\r
6655 move[1] = NULLCHAR;
\r
6656 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6657 Translate(hDlg, DLG_TypeInName);
\r
6658 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6659 SetWindowText(hInput, move);
\r
6661 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6665 switch (LOWORD(wParam)) {
\r
6667 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6668 appData.userName = strdup(move);
\r
6671 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6672 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6673 DisplayTitle(move);
\r
6677 EndDialog(hDlg, TRUE);
\r
6680 EndDialog(hDlg, FALSE);
\r
6691 PopUpNameDialog(char firstchar)
\r
6695 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6696 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6697 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6698 FreeProcInstance(lpProc);
\r
6701 /*---------------------------------------------------------------------------*\
\r
6705 \*---------------------------------------------------------------------------*/
\r
6707 /* Nonmodal error box */
\r
6708 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6709 WPARAM wParam, LPARAM lParam);
\r
6712 ErrorPopUp(char *title, char *content)
\r
6716 BOOLEAN modal = hwndMain == NULL;
\r
6734 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6735 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6738 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6740 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6741 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6742 hwndMain, (DLGPROC)lpProc);
\r
6743 FreeProcInstance(lpProc);
\r
6750 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6751 if (errorDialog == NULL) return;
\r
6752 DestroyWindow(errorDialog);
\r
6753 errorDialog = NULL;
\r
6754 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6758 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6762 switch (message) {
\r
6763 case WM_INITDIALOG:
\r
6764 GetWindowRect(hDlg, &rChild);
\r
6767 SetWindowPos(hDlg, NULL, rChild.left,
\r
6768 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6769 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6773 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6774 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6775 and it doesn't work when you resize the dialog.
\r
6776 For now, just give it a default position.
\r
6778 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6779 Translate(hDlg, DLG_Error);
\r
6781 errorDialog = hDlg;
\r
6782 SetWindowText(hDlg, errorTitle);
\r
6783 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6787 switch (LOWORD(wParam)) {
\r
6790 if (errorDialog == hDlg) errorDialog = NULL;
\r
6791 DestroyWindow(hDlg);
\r
6803 HWND gothicDialog = NULL;
\r
6806 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6809 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6811 switch (message) {
\r
6812 case WM_INITDIALOG:
\r
6813 GetWindowRect(hDlg, &rChild);
\r
6815 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6819 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6820 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6821 and it doesn't work when you resize the dialog.
\r
6822 For now, just give it a default position.
\r
6824 gothicDialog = hDlg;
\r
6825 SetWindowText(hDlg, errorTitle);
\r
6826 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6830 switch (LOWORD(wParam)) {
\r
6833 if (errorDialog == hDlg) errorDialog = NULL;
\r
6834 DestroyWindow(hDlg);
\r
6846 GothicPopUp(char *title, VariantClass variant)
\r
6849 static char *lastTitle;
\r
6851 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6852 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6854 if(lastTitle != title && gothicDialog != NULL) {
\r
6855 DestroyWindow(gothicDialog);
\r
6856 gothicDialog = NULL;
\r
6858 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6859 title = lastTitle;
\r
6860 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6861 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6862 hwndMain, (DLGPROC)lpProc);
\r
6863 FreeProcInstance(lpProc);
\r
6868 /*---------------------------------------------------------------------------*\
\r
6870 * Ics Interaction console functions
\r
6872 \*---------------------------------------------------------------------------*/
\r
6874 #define HISTORY_SIZE 64
\r
6875 static char *history[HISTORY_SIZE];
\r
6876 int histIn = 0, histP = 0;
\r
6880 SaveInHistory(char *cmd)
\r
6882 if (history[histIn] != NULL) {
\r
6883 free(history[histIn]);
\r
6884 history[histIn] = NULL;
\r
6886 if (*cmd == NULLCHAR) return;
\r
6887 history[histIn] = StrSave(cmd);
\r
6888 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6889 if (history[histIn] != NULL) {
\r
6890 free(history[histIn]);
\r
6892 history[histIn] = NULL;
\r
6898 PrevInHistory(char *cmd)
\r
6901 if (histP == histIn) {
\r
6902 if (history[histIn] != NULL) free(history[histIn]);
\r
6903 history[histIn] = StrSave(cmd);
\r
6905 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6906 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6908 return history[histP];
\r
6914 if (histP == histIn) return NULL;
\r
6915 histP = (histP + 1) % HISTORY_SIZE;
\r
6916 return history[histP];
\r
6920 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6924 hmenu = LoadMenu(hInst, "TextMenu");
\r
6925 h = GetSubMenu(hmenu, 0);
\r
6927 if (strcmp(e->item, "-") == 0) {
\r
6928 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6929 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6930 int flags = MF_STRING, j = 0;
\r
6931 if (e->item[0] == '|') {
\r
6932 flags |= MF_MENUBARBREAK;
\r
6935 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6936 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6944 WNDPROC consoleTextWindowProc;
\r
6947 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6949 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6950 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6954 SetWindowText(hInput, command);
\r
6956 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6958 sel.cpMin = 999999;
\r
6959 sel.cpMax = 999999;
\r
6960 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6965 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6966 if (sel.cpMin == sel.cpMax) {
\r
6967 /* Expand to surrounding word */
\r
6970 tr.chrg.cpMax = sel.cpMin;
\r
6971 tr.chrg.cpMin = --sel.cpMin;
\r
6972 if (sel.cpMin < 0) break;
\r
6973 tr.lpstrText = name;
\r
6974 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6975 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6979 tr.chrg.cpMin = sel.cpMax;
\r
6980 tr.chrg.cpMax = ++sel.cpMax;
\r
6981 tr.lpstrText = name;
\r
6982 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6983 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6986 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6987 MessageBeep(MB_ICONEXCLAMATION);
\r
6991 tr.lpstrText = name;
\r
6992 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6994 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6995 MessageBeep(MB_ICONEXCLAMATION);
\r
6998 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
7001 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
7002 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
7003 SetWindowText(hInput, buf);
\r
7004 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
7006 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
7007 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
7008 SetWindowText(hInput, buf);
\r
7009 sel.cpMin = 999999;
\r
7010 sel.cpMax = 999999;
\r
7011 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7017 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7022 switch (message) {
\r
7024 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7025 if(wParam=='R') return 0;
\r
7028 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
7031 sel.cpMin = 999999;
\r
7032 sel.cpMax = 999999;
\r
7033 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7034 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
7039 if(wParam != '\022') {
\r
7040 if (wParam == '\t') {
\r
7041 if (GetKeyState(VK_SHIFT) < 0) {
\r
7043 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7044 if (buttonDesc[0].hwnd) {
\r
7045 SetFocus(buttonDesc[0].hwnd);
\r
7047 SetFocus(hwndMain);
\r
7051 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
7054 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7055 JAWS_DELETE( SetFocus(hInput); )
\r
7056 SendMessage(hInput, message, wParam, lParam);
\r
7059 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
7061 case WM_RBUTTONDOWN:
\r
7062 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
7063 /* Move selection here if it was empty */
\r
7065 pt.x = LOWORD(lParam);
\r
7066 pt.y = HIWORD(lParam);
\r
7067 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7068 if (sel.cpMin == sel.cpMax) {
\r
7069 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
7070 sel.cpMax = sel.cpMin;
\r
7071 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7073 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
7074 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
7076 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
7077 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7078 if (sel.cpMin == sel.cpMax) {
\r
7079 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7080 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
7082 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7083 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7085 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
7086 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
7087 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
7088 MenuPopup(hwnd, pt, hmenu, -1);
\r
7092 case WM_RBUTTONUP:
\r
7093 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7094 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7095 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7099 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7101 return SendMessage(hInput, message, wParam, lParam);
\r
7102 case WM_MBUTTONDOWN:
\r
7103 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7105 switch (LOWORD(wParam)) {
\r
7106 case IDM_QuickPaste:
\r
7108 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7109 if (sel.cpMin == sel.cpMax) {
\r
7110 MessageBeep(MB_ICONEXCLAMATION);
\r
7113 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7114 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7115 SendMessage(hInput, WM_PASTE, 0, 0);
\r
7120 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7123 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7126 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7130 int i = LOWORD(wParam) - IDM_CommandX;
\r
7131 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
7132 icsTextMenuEntry[i].command != NULL) {
\r
7133 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
7134 icsTextMenuEntry[i].getname,
\r
7135 icsTextMenuEntry[i].immediate);
\r
7143 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
7146 WNDPROC consoleInputWindowProc;
\r
7149 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7151 char buf[MSG_SIZ];
\r
7153 static BOOL sendNextChar = FALSE;
\r
7154 static BOOL quoteNextChar = FALSE;
\r
7155 InputSource *is = consoleInputSource;
\r
7159 switch (message) {
\r
7161 if (!appData.localLineEditing || sendNextChar) {
\r
7162 is->buf[0] = (CHAR) wParam;
\r
7164 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7165 sendNextChar = FALSE;
\r
7168 if (quoteNextChar) {
\r
7169 buf[0] = (char) wParam;
\r
7170 buf[1] = NULLCHAR;
\r
7171 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7172 quoteNextChar = FALSE;
\r
7176 case '\r': /* Enter key */
\r
7177 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7178 if (consoleEcho) SaveInHistory(is->buf);
\r
7179 is->buf[is->count++] = '\n';
\r
7180 is->buf[is->count] = NULLCHAR;
\r
7181 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7182 if (consoleEcho) {
\r
7183 ConsoleOutput(is->buf, is->count, TRUE);
\r
7184 } else if (appData.localLineEditing) {
\r
7185 ConsoleOutput("\n", 1, TRUE);
\r
7188 case '\033': /* Escape key */
\r
7189 SetWindowText(hwnd, "");
\r
7190 cf.cbSize = sizeof(CHARFORMAT);
\r
7191 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7192 if (consoleEcho) {
\r
7193 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7195 cf.crTextColor = COLOR_ECHOOFF;
\r
7197 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7198 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7200 case '\t': /* Tab key */
\r
7201 if (GetKeyState(VK_SHIFT) < 0) {
\r
7203 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7206 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7207 if (buttonDesc[0].hwnd) {
\r
7208 SetFocus(buttonDesc[0].hwnd);
\r
7210 SetFocus(hwndMain);
\r
7214 case '\023': /* Ctrl+S */
\r
7215 sendNextChar = TRUE;
\r
7217 case '\021': /* Ctrl+Q */
\r
7218 quoteNextChar = TRUE;
\r
7228 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7229 p = PrevInHistory(buf);
\r
7231 SetWindowText(hwnd, p);
\r
7232 sel.cpMin = 999999;
\r
7233 sel.cpMax = 999999;
\r
7234 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7239 p = NextInHistory();
\r
7241 SetWindowText(hwnd, p);
\r
7242 sel.cpMin = 999999;
\r
7243 sel.cpMax = 999999;
\r
7244 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7250 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7254 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7258 case WM_MBUTTONDOWN:
\r
7259 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7260 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7262 case WM_RBUTTONUP:
\r
7263 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7264 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7265 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7269 hmenu = LoadMenu(hInst, "InputMenu");
\r
7270 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7271 if (sel.cpMin == sel.cpMax) {
\r
7272 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7273 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7275 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7276 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7278 pt.x = LOWORD(lParam);
\r
7279 pt.y = HIWORD(lParam);
\r
7280 MenuPopup(hwnd, pt, hmenu, -1);
\r
7284 switch (LOWORD(wParam)) {
\r
7286 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7288 case IDM_SelectAll:
\r
7290 sel.cpMax = -1; /*999999?*/
\r
7291 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7294 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7297 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7300 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7305 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7308 #define CO_MAX 100000
\r
7309 #define CO_TRIM 1000
\r
7312 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7314 static SnapData sd;
\r
7315 HWND hText, hInput;
\r
7317 static int sizeX, sizeY;
\r
7318 int newSizeX, newSizeY;
\r
7322 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7323 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7325 switch (message) {
\r
7327 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7329 ENLINK *pLink = (ENLINK*)lParam;
\r
7330 if (pLink->msg == WM_LBUTTONUP)
\r
7334 tr.chrg = pLink->chrg;
\r
7335 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7336 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7337 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7338 free(tr.lpstrText);
\r
7342 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7343 hwndConsole = hDlg;
\r
7345 consoleTextWindowProc = (WNDPROC)
\r
7346 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7347 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7348 consoleInputWindowProc = (WNDPROC)
\r
7349 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7350 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7351 Colorize(ColorNormal, TRUE);
\r
7352 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7353 ChangedConsoleFont();
\r
7354 GetClientRect(hDlg, &rect);
\r
7355 sizeX = rect.right;
\r
7356 sizeY = rect.bottom;
\r
7357 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7358 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7359 WINDOWPLACEMENT wp;
\r
7360 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7361 wp.length = sizeof(WINDOWPLACEMENT);
\r
7363 wp.showCmd = SW_SHOW;
\r
7364 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7365 wp.rcNormalPosition.left = wpConsole.x;
\r
7366 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7367 wp.rcNormalPosition.top = wpConsole.y;
\r
7368 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7369 SetWindowPlacement(hDlg, &wp);
\r
7372 // [HGM] Chessknight's change 2004-07-13
\r
7373 else { /* Determine Defaults */
\r
7374 WINDOWPLACEMENT wp;
\r
7375 wpConsole.x = wpMain.width + 1;
\r
7376 wpConsole.y = wpMain.y;
\r
7377 wpConsole.width = screenWidth - wpMain.width;
\r
7378 wpConsole.height = wpMain.height;
\r
7379 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7380 wp.length = sizeof(WINDOWPLACEMENT);
\r
7382 wp.showCmd = SW_SHOW;
\r
7383 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7384 wp.rcNormalPosition.left = wpConsole.x;
\r
7385 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7386 wp.rcNormalPosition.top = wpConsole.y;
\r
7387 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7388 SetWindowPlacement(hDlg, &wp);
\r
7391 // Allow hText to highlight URLs and send notifications on them
\r
7392 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7393 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7394 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7395 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7409 if (IsIconic(hDlg)) break;
\r
7410 newSizeX = LOWORD(lParam);
\r
7411 newSizeY = HIWORD(lParam);
\r
7412 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7413 RECT rectText, rectInput;
\r
7415 int newTextHeight, newTextWidth;
\r
7416 GetWindowRect(hText, &rectText);
\r
7417 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7418 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7419 if (newTextHeight < 0) {
\r
7420 newSizeY += -newTextHeight;
\r
7421 newTextHeight = 0;
\r
7423 SetWindowPos(hText, NULL, 0, 0,
\r
7424 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7425 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7426 pt.x = rectInput.left;
\r
7427 pt.y = rectInput.top + newSizeY - sizeY;
\r
7428 ScreenToClient(hDlg, &pt);
\r
7429 SetWindowPos(hInput, NULL,
\r
7430 pt.x, pt.y, /* needs client coords */
\r
7431 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7432 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7438 case WM_GETMINMAXINFO:
\r
7439 /* Prevent resizing window too small */
\r
7440 mmi = (MINMAXINFO *) lParam;
\r
7441 mmi->ptMinTrackSize.x = 100;
\r
7442 mmi->ptMinTrackSize.y = 100;
\r
7445 /* [AS] Snapping */
\r
7446 case WM_ENTERSIZEMOVE:
\r
7447 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7450 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7453 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7455 case WM_EXITSIZEMOVE:
\r
7456 UpdateICSWidth(hText);
\r
7457 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7460 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7468 if (hwndConsole) return;
\r
7469 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7470 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7475 ConsoleOutput(char* data, int length, int forceVisible)
\r
7480 char buf[CO_MAX+1];
\r
7483 static int delayLF = 0;
\r
7484 CHARRANGE savesel, sel;
\r
7486 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7494 while (length--) {
\r
7502 } else if (*p == '\007') {
\r
7503 MyPlaySound(&sounds[(int)SoundBell]);
\r
7510 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7511 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7512 /* Save current selection */
\r
7513 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7514 exlen = GetWindowTextLength(hText);
\r
7515 /* Find out whether current end of text is visible */
\r
7516 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7517 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7518 /* Trim existing text if it's too long */
\r
7519 if (exlen + (q - buf) > CO_MAX) {
\r
7520 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7523 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7524 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7526 savesel.cpMin -= trim;
\r
7527 savesel.cpMax -= trim;
\r
7528 if (exlen < 0) exlen = 0;
\r
7529 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7530 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7532 /* Append the new text */
\r
7533 sel.cpMin = exlen;
\r
7534 sel.cpMax = exlen;
\r
7535 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7536 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7537 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7538 if (forceVisible || exlen == 0 ||
\r
7539 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7540 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7541 /* Scroll to make new end of text visible if old end of text
\r
7542 was visible or new text is an echo of user typein */
\r
7543 sel.cpMin = 9999999;
\r
7544 sel.cpMax = 9999999;
\r
7545 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7546 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7547 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7548 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7550 if (savesel.cpMax == exlen || forceVisible) {
\r
7551 /* Move insert point to new end of text if it was at the old
\r
7552 end of text or if the new text is an echo of user typein */
\r
7553 sel.cpMin = 9999999;
\r
7554 sel.cpMax = 9999999;
\r
7555 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7557 /* Restore previous selection */
\r
7558 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7560 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7567 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7571 COLORREF oldFg, oldBg;
\r
7575 if(copyNumber > 1)
\r
7576 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7578 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7579 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7580 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7583 rect.right = x + squareSize;
\r
7585 rect.bottom = y + squareSize;
\r
7588 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7589 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7590 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7591 &rect, str, strlen(str), NULL);
\r
7593 (void) SetTextColor(hdc, oldFg);
\r
7594 (void) SetBkColor(hdc, oldBg);
\r
7595 (void) SelectObject(hdc, oldFont);
\r
7599 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7600 RECT *rect, char *color, char *flagFell)
\r
7604 COLORREF oldFg, oldBg;
\r
7607 if (twoBoards && partnerUp) return;
\r
7608 if (appData.clockMode) {
\r
7610 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7612 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7619 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7620 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7622 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7623 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7625 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7629 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7630 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7631 rect, str, strlen(str), NULL);
\r
7632 if(logoHeight > 0 && appData.clockMode) {
\r
7634 str += strlen(color)+2;
\r
7635 r.top = rect->top + logoHeight/2;
\r
7636 r.left = rect->left;
\r
7637 r.right = rect->right;
\r
7638 r.bottom = rect->bottom;
\r
7639 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7640 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7641 &r, str, strlen(str), NULL);
\r
7643 (void) SetTextColor(hdc, oldFg);
\r
7644 (void) SetBkColor(hdc, oldBg);
\r
7645 (void) SelectObject(hdc, oldFont);
\r
7650 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7656 if( count <= 0 ) {
\r
7657 if (appData.debugMode) {
\r
7658 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7661 return ERROR_INVALID_USER_BUFFER;
\r
7664 ResetEvent(ovl->hEvent);
\r
7665 ovl->Offset = ovl->OffsetHigh = 0;
\r
7666 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7670 err = GetLastError();
\r
7671 if (err == ERROR_IO_PENDING) {
\r
7672 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7676 err = GetLastError();
\r
7683 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7688 ResetEvent(ovl->hEvent);
\r
7689 ovl->Offset = ovl->OffsetHigh = 0;
\r
7690 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7694 err = GetLastError();
\r
7695 if (err == ERROR_IO_PENDING) {
\r
7696 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7700 err = GetLastError();
\r
7707 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7708 void CheckForInputBufferFull( InputSource * is )
\r
7710 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7711 /* Look for end of line */
\r
7712 char * p = is->buf;
\r
7714 while( p < is->next && *p != '\n' ) {
\r
7718 if( p >= is->next ) {
\r
7719 if (appData.debugMode) {
\r
7720 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7723 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7724 is->count = (DWORD) -1;
\r
7725 is->next = is->buf;
\r
7731 InputThread(LPVOID arg)
\r
7736 is = (InputSource *) arg;
\r
7737 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7738 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7739 while (is->hThread != NULL) {
\r
7740 is->error = DoReadFile(is->hFile, is->next,
\r
7741 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7742 &is->count, &ovl);
\r
7743 if (is->error == NO_ERROR) {
\r
7744 is->next += is->count;
\r
7746 if (is->error == ERROR_BROKEN_PIPE) {
\r
7747 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7750 is->count = (DWORD) -1;
\r
7751 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7756 CheckForInputBufferFull( is );
\r
7758 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7760 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7762 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7765 CloseHandle(ovl.hEvent);
\r
7766 CloseHandle(is->hFile);
\r
7768 if (appData.debugMode) {
\r
7769 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7776 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7778 NonOvlInputThread(LPVOID arg)
\r
7785 is = (InputSource *) arg;
\r
7786 while (is->hThread != NULL) {
\r
7787 is->error = ReadFile(is->hFile, is->next,
\r
7788 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7789 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7790 if (is->error == NO_ERROR) {
\r
7791 /* Change CRLF to LF */
\r
7792 if (is->next > is->buf) {
\r
7794 i = is->count + 1;
\r
7802 if (prev == '\r' && *p == '\n') {
\r
7814 if (is->error == ERROR_BROKEN_PIPE) {
\r
7815 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7818 is->count = (DWORD) -1;
\r
7822 CheckForInputBufferFull( is );
\r
7824 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7826 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7828 if (is->count < 0) break; /* Quit on error */
\r
7830 CloseHandle(is->hFile);
\r
7835 SocketInputThread(LPVOID arg)
\r
7839 is = (InputSource *) arg;
\r
7840 while (is->hThread != NULL) {
\r
7841 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7842 if ((int)is->count == SOCKET_ERROR) {
\r
7843 is->count = (DWORD) -1;
\r
7844 is->error = WSAGetLastError();
\r
7846 is->error = NO_ERROR;
\r
7847 is->next += is->count;
\r
7848 if (is->count == 0 && is->second == is) {
\r
7849 /* End of file on stderr; quit with no message */
\r
7853 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7855 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7857 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7863 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7867 is = (InputSource *) lParam;
\r
7868 if (is->lineByLine) {
\r
7869 /* Feed in lines one by one */
\r
7870 char *p = is->buf;
\r
7872 while (q < is->next) {
\r
7873 if (*q++ == '\n') {
\r
7874 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7879 /* Move any partial line to the start of the buffer */
\r
7881 while (p < is->next) {
\r
7886 if (is->error != NO_ERROR || is->count == 0) {
\r
7887 /* Notify backend of the error. Note: If there was a partial
\r
7888 line at the end, it is not flushed through. */
\r
7889 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7892 /* Feed in the whole chunk of input at once */
\r
7893 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7894 is->next = is->buf;
\r
7898 /*---------------------------------------------------------------------------*\
\r
7900 * Menu enables. Used when setting various modes.
\r
7902 \*---------------------------------------------------------------------------*/
\r
7910 GreyRevert(Boolean grey)
\r
7911 { // [HGM] vari: for retracting variations in local mode
\r
7912 HMENU hmenu = GetMenu(hwndMain);
\r
7913 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7914 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7918 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7920 while (enab->item > 0) {
\r
7921 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7926 Enables gnuEnables[] = {
\r
7927 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7928 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7929 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7930 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7931 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7932 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7933 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7934 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7935 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7936 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7937 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7938 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7939 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7941 // Needed to switch from ncp to GNU mode on Engine Load
\r
7942 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7943 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7944 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7945 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7946 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7947 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7948 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
7949 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7950 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
7951 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
7952 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7953 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7954 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7955 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7959 Enables icsEnables[] = {
\r
7960 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7961 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7962 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7963 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7964 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7965 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7966 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7967 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7968 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7969 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7970 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7971 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7972 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7973 { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },
\r
7974 { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },
\r
7975 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7976 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7977 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7978 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7979 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
7984 Enables zippyEnables[] = {
\r
7985 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7986 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7987 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7988 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7993 Enables ncpEnables[] = {
\r
7994 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7995 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7996 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7997 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7998 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7999 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8000 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
8001 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
8002 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
8003 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
8004 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
8005 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
8006 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8007 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
8008 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
8009 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
8010 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
8011 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
8012 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
8013 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
8014 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
8015 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
8019 Enables trainingOnEnables[] = {
\r
8020 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
8021 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
8022 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
8023 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
8024 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
8025 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
8026 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
8027 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8028 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
8032 Enables trainingOffEnables[] = {
\r
8033 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
8034 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
8035 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
8036 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
8037 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
8038 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
8039 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
8040 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8041 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
8045 /* These modify either ncpEnables or gnuEnables */
\r
8046 Enables cmailEnables[] = {
\r
8047 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
8048 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
8049 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
8050 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
8051 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
8052 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
8053 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
8057 Enables machineThinkingEnables[] = {
\r
8058 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8059 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
8060 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
8061 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8062 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
8063 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8064 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8065 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8066 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8067 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
8068 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8069 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8070 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8071 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8072 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
8073 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
8077 Enables userThinkingEnables[] = {
\r
8078 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8079 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
8080 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
8081 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8082 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
8083 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8084 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8085 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8086 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8087 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
8088 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
8089 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
8090 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
8091 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
8092 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
8093 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
8097 /*---------------------------------------------------------------------------*\
\r
8099 * Front-end interface functions exported by XBoard.
\r
8100 * Functions appear in same order as prototypes in frontend.h.
\r
8102 \*---------------------------------------------------------------------------*/
\r
8104 CheckMark(UINT item, int state)
\r
8106 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
8112 static UINT prevChecked = 0;
\r
8113 static int prevPausing = 0;
\r
8116 if (pausing != prevPausing) {
\r
8117 prevPausing = pausing;
\r
8118 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
8119 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
8120 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
8123 switch (gameMode) {
\r
8124 case BeginningOfGame:
\r
8125 if (appData.icsActive)
\r
8126 nowChecked = IDM_IcsClient;
\r
8127 else if (appData.noChessProgram)
\r
8128 nowChecked = IDM_EditGame;
\r
8130 nowChecked = IDM_MachineBlack;
\r
8132 case MachinePlaysBlack:
\r
8133 nowChecked = IDM_MachineBlack;
\r
8135 case MachinePlaysWhite:
\r
8136 nowChecked = IDM_MachineWhite;
\r
8138 case TwoMachinesPlay:
\r
8139 nowChecked = IDM_TwoMachines;
\r
8142 nowChecked = IDM_AnalysisMode;
\r
8145 nowChecked = IDM_AnalyzeFile;
\r
8148 nowChecked = IDM_EditGame;
\r
8150 case PlayFromGameFile:
\r
8151 nowChecked = IDM_LoadGame;
\r
8153 case EditPosition:
\r
8154 nowChecked = IDM_EditPosition;
\r
8157 nowChecked = IDM_Training;
\r
8159 case IcsPlayingWhite:
\r
8160 case IcsPlayingBlack:
\r
8161 case IcsObserving:
\r
8163 nowChecked = IDM_IcsClient;
\r
8170 CheckMark(prevChecked, MF_UNCHECKED);
\r
8171 CheckMark(nowChecked, MF_CHECKED);
\r
8172 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8174 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8175 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8176 MF_BYCOMMAND|MF_ENABLED);
\r
8178 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8179 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8182 prevChecked = nowChecked;
\r
8184 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8185 if (appData.icsActive) {
\r
8186 if (appData.icsEngineAnalyze) {
\r
8187 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8189 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8192 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8198 HMENU hmenu = GetMenu(hwndMain);
\r
8199 SetMenuEnables(hmenu, icsEnables);
\r
8200 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8201 MF_BYCOMMAND|MF_ENABLED);
\r
8203 if (appData.zippyPlay) {
\r
8204 SetMenuEnables(hmenu, zippyEnables);
\r
8205 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8206 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8207 MF_BYCOMMAND|MF_ENABLED);
\r
8215 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8221 HMENU hmenu = GetMenu(hwndMain);
\r
8222 SetMenuEnables(hmenu, ncpEnables);
\r
8223 DrawMenuBar(hwndMain);
\r
8229 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8233 SetTrainingModeOn()
\r
8236 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8237 for (i = 0; i < N_BUTTONS; i++) {
\r
8238 if (buttonDesc[i].hwnd != NULL)
\r
8239 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8244 VOID SetTrainingModeOff()
\r
8247 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8248 for (i = 0; i < N_BUTTONS; i++) {
\r
8249 if (buttonDesc[i].hwnd != NULL)
\r
8250 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8256 SetUserThinkingEnables()
\r
8258 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8262 SetMachineThinkingEnables()
\r
8264 HMENU hMenu = GetMenu(hwndMain);
\r
8265 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8267 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8269 if (gameMode == MachinePlaysBlack) {
\r
8270 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8271 } else if (gameMode == MachinePlaysWhite) {
\r
8272 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8273 } else if (gameMode == TwoMachinesPlay) {
\r
8274 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8280 DisplayTitle(char *str)
\r
8282 char title[MSG_SIZ], *host;
\r
8283 if (str[0] != NULLCHAR) {
\r
8284 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8285 } else if (appData.icsActive) {
\r
8286 if (appData.icsCommPort[0] != NULLCHAR)
\r
8289 host = appData.icsHost;
\r
8290 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8291 } else if (appData.noChessProgram) {
\r
8292 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8294 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8295 strcat(title, ": ");
\r
8296 strcat(title, first.tidy);
\r
8298 SetWindowText(hwndMain, title);
\r
8303 DisplayMessage(char *str1, char *str2)
\r
8307 int remain = MESSAGE_TEXT_MAX - 1;
\r
8310 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8311 messageText[0] = NULLCHAR;
\r
8313 len = strlen(str1);
\r
8314 if (len > remain) len = remain;
\r
8315 strncpy(messageText, str1, len);
\r
8316 messageText[len] = NULLCHAR;
\r
8319 if (*str2 && remain >= 2) {
\r
8321 strcat(messageText, " ");
\r
8324 len = strlen(str2);
\r
8325 if (len > remain) len = remain;
\r
8326 strncat(messageText, str2, len);
\r
8328 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8329 safeStrCpy(lastMsg, messageText, MSG_SIZ);
\r
8331 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8335 hdc = GetDC(hwndMain);
\r
8336 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8337 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8338 &messageRect, messageText, strlen(messageText), NULL);
\r
8339 (void) SelectObject(hdc, oldFont);
\r
8340 (void) ReleaseDC(hwndMain, hdc);
\r
8344 DisplayError(char *str, int error)
\r
8346 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8350 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8352 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8353 NULL, error, LANG_NEUTRAL,
\r
8354 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8356 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8358 ErrorMap *em = errmap;
\r
8359 while (em->err != 0 && em->err != error) em++;
\r
8360 if (em->err != 0) {
\r
8361 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8363 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8368 ErrorPopUp(_("Error"), buf);
\r
8373 DisplayMoveError(char *str)
\r
8375 fromX = fromY = -1;
\r
8376 ClearHighlights();
\r
8377 DrawPosition(FALSE, NULL);
\r
8378 if (appData.popupMoveErrors) {
\r
8379 ErrorPopUp(_("Error"), str);
\r
8381 DisplayMessage(str, "");
\r
8382 moveErrorMessageUp = TRUE;
\r
8387 DisplayFatalError(char *str, int error, int exitStatus)
\r
8389 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8391 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8394 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8395 NULL, error, LANG_NEUTRAL,
\r
8396 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8398 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8400 ErrorMap *em = errmap;
\r
8401 while (em->err != 0 && em->err != error) em++;
\r
8402 if (em->err != 0) {
\r
8403 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8405 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8410 if (appData.debugMode) {
\r
8411 fprintf(debugFP, "%s: %s\n", label, str);
\r
8413 if (appData.popupExitMessage) {
\r
8414 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8415 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8417 ExitEvent(exitStatus);
\r
8422 DisplayInformation(char *str)
\r
8424 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8429 DisplayNote(char *str)
\r
8431 ErrorPopUp(_("Note"), str);
\r
8436 char *title, *question, *replyPrefix;
\r
8441 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8443 static QuestionParams *qp;
\r
8444 char reply[MSG_SIZ];
\r
8447 switch (message) {
\r
8448 case WM_INITDIALOG:
\r
8449 qp = (QuestionParams *) lParam;
\r
8450 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8451 Translate(hDlg, DLG_Question);
\r
8452 SetWindowText(hDlg, qp->title);
\r
8453 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8454 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8458 switch (LOWORD(wParam)) {
\r
8460 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8461 if (*reply) strcat(reply, " ");
\r
8462 len = strlen(reply);
\r
8463 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8464 strcat(reply, "\n");
\r
8465 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8466 EndDialog(hDlg, TRUE);
\r
8467 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8470 EndDialog(hDlg, FALSE);
\r
8481 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8483 QuestionParams qp;
\r
8487 qp.question = question;
\r
8488 qp.replyPrefix = replyPrefix;
\r
8490 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8491 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8492 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8493 FreeProcInstance(lpProc);
\r
8496 /* [AS] Pick FRC position */
\r
8497 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8499 static int * lpIndexFRC;
\r
8505 case WM_INITDIALOG:
\r
8506 lpIndexFRC = (int *) lParam;
\r
8508 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8509 Translate(hDlg, DLG_NewGameFRC);
\r
8511 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8512 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8513 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8514 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8519 switch( LOWORD(wParam) ) {
\r
8521 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8522 EndDialog( hDlg, 0 );
\r
8523 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8526 EndDialog( hDlg, 1 );
\r
8528 case IDC_NFG_Edit:
\r
8529 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8530 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8532 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8535 case IDC_NFG_Random:
\r
8536 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8537 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8550 int index = appData.defaultFrcPosition;
\r
8551 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8553 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8555 if( result == 0 ) {
\r
8556 appData.defaultFrcPosition = index;
\r
8562 /* [AS] Game list options. Refactored by HGM */
\r
8564 HWND gameListOptionsDialog;
\r
8566 // low-level front-end: clear text edit / list widget
\r
8571 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8574 // low-level front-end: clear text edit / list widget
\r
8576 GLT_DeSelectList()
\r
8578 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8581 // low-level front-end: append line to text edit / list widget
\r
8583 GLT_AddToList( char *name )
\r
8586 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8590 // low-level front-end: get line from text edit / list widget
\r
8592 GLT_GetFromList( int index, char *name )
\r
8595 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8601 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8603 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8604 int idx2 = idx1 + delta;
\r
8605 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8607 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8610 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8611 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8612 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8613 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8617 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8621 case WM_INITDIALOG:
\r
8622 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8624 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8625 Translate(hDlg, DLG_GameListOptions);
\r
8627 /* Initialize list */
\r
8628 GLT_TagsToList( lpUserGLT );
\r
8630 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8635 switch( LOWORD(wParam) ) {
\r
8638 EndDialog( hDlg, 0 );
\r
8641 EndDialog( hDlg, 1 );
\r
8644 case IDC_GLT_Default:
\r
8645 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8648 case IDC_GLT_Restore:
\r
8649 GLT_TagsToList( appData.gameListTags );
\r
8653 GLT_MoveSelection( hDlg, -1 );
\r
8656 case IDC_GLT_Down:
\r
8657 GLT_MoveSelection( hDlg, +1 );
\r
8667 int GameListOptions()
\r
8670 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8672 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8674 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8676 if( result == 0 ) {
\r
8677 /* [AS] Memory leak here! */
\r
8678 appData.gameListTags = strdup( lpUserGLT );
\r
8685 DisplayIcsInteractionTitle(char *str)
\r
8687 char consoleTitle[MSG_SIZ];
\r
8689 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8690 SetWindowText(hwndConsole, consoleTitle);
\r
8692 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
8693 char buf[MSG_SIZ], *p = buf, *q;
\r
8694 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
8696 q = strchr(p, ';');
\r
8698 if(*p) ChatPopUp(p);
\r
8702 SetActiveWindow(hwndMain);
\r
8706 DrawPosition(int fullRedraw, Board board)
\r
8708 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8711 void NotifyFrontendLogin()
\r
8714 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8720 fromX = fromY = -1;
\r
8721 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8722 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8723 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8724 dragInfo.lastpos = dragInfo.pos;
\r
8725 dragInfo.start.x = dragInfo.start.y = -1;
\r
8726 dragInfo.from = dragInfo.start;
\r
8728 DrawPosition(TRUE, NULL);
\r
8735 CommentPopUp(char *title, char *str)
\r
8737 HWND hwnd = GetActiveWindow();
\r
8738 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8740 SetActiveWindow(hwnd);
\r
8744 CommentPopDown(void)
\r
8746 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8747 if (commentDialog) {
\r
8748 ShowWindow(commentDialog, SW_HIDE);
\r
8750 commentUp = FALSE;
\r
8754 EditCommentPopUp(int index, char *title, char *str)
\r
8756 EitherCommentPopUp(index, title, str, TRUE);
\r
8763 MyPlaySound(&sounds[(int)SoundRoar]);
\r
8770 MyPlaySound(&sounds[(int)SoundMove]);
\r
8773 VOID PlayIcsWinSound()
\r
8775 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8778 VOID PlayIcsLossSound()
\r
8780 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8783 VOID PlayIcsDrawSound()
\r
8785 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8788 VOID PlayIcsUnfinishedSound()
\r
8790 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8796 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8802 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8810 consoleEcho = TRUE;
\r
8811 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8812 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8813 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8822 consoleEcho = FALSE;
\r
8823 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8824 /* This works OK: set text and background both to the same color */
\r
8826 cf.crTextColor = COLOR_ECHOOFF;
\r
8827 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8828 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8831 /* No Raw()...? */
\r
8833 void Colorize(ColorClass cc, int continuation)
\r
8835 currentColorClass = cc;
\r
8836 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8837 consoleCF.crTextColor = textAttribs[cc].color;
\r
8838 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8839 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8845 static char buf[MSG_SIZ];
\r
8846 DWORD bufsiz = MSG_SIZ;
\r
8848 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8849 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8851 if (!GetUserName(buf, &bufsiz)) {
\r
8852 /*DisplayError("Error getting user name", GetLastError());*/
\r
8853 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8861 static char buf[MSG_SIZ];
\r
8862 DWORD bufsiz = MSG_SIZ;
\r
8864 if (!GetComputerName(buf, &bufsiz)) {
\r
8865 /*DisplayError("Error getting host name", GetLastError());*/
\r
8866 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8873 ClockTimerRunning()
\r
8875 return clockTimerEvent != 0;
\r
8881 if (clockTimerEvent == 0) return FALSE;
\r
8882 KillTimer(hwndMain, clockTimerEvent);
\r
8883 clockTimerEvent = 0;
\r
8888 StartClockTimer(long millisec)
\r
8890 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8891 (UINT) millisec, NULL);
\r
8895 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8898 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8900 if(appData.noGUI) return;
\r
8901 hdc = GetDC(hwndMain);
\r
8902 if (!IsIconic(hwndMain)) {
\r
8903 DisplayAClock(hdc, timeRemaining, highlight,
\r
8904 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8906 if (highlight && iconCurrent == iconBlack) {
\r
8907 iconCurrent = iconWhite;
\r
8908 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8909 if (IsIconic(hwndMain)) {
\r
8910 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8913 (void) ReleaseDC(hwndMain, hdc);
\r
8915 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8919 DisplayBlackClock(long timeRemaining, int highlight)
\r
8922 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8925 if(appData.noGUI) return;
\r
8926 hdc = GetDC(hwndMain);
\r
8927 if (!IsIconic(hwndMain)) {
\r
8928 DisplayAClock(hdc, timeRemaining, highlight,
\r
8929 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8931 if (highlight && iconCurrent == iconWhite) {
\r
8932 iconCurrent = iconBlack;
\r
8933 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8934 if (IsIconic(hwndMain)) {
\r
8935 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8938 (void) ReleaseDC(hwndMain, hdc);
\r
8940 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8945 LoadGameTimerRunning()
\r
8947 return loadGameTimerEvent != 0;
\r
8951 StopLoadGameTimer()
\r
8953 if (loadGameTimerEvent == 0) return FALSE;
\r
8954 KillTimer(hwndMain, loadGameTimerEvent);
\r
8955 loadGameTimerEvent = 0;
\r
8960 StartLoadGameTimer(long millisec)
\r
8962 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8963 (UINT) millisec, NULL);
\r
8971 char fileTitle[MSG_SIZ];
\r
8973 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8974 f = OpenFileDialog(hwndMain, "a", defName,
\r
8975 appData.oldSaveStyle ? "gam" : "pgn",
\r
8977 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8979 SaveGame(f, 0, "");
\r
8986 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8988 if (delayedTimerEvent != 0) {
\r
8989 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8990 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8992 KillTimer(hwndMain, delayedTimerEvent);
\r
8993 delayedTimerEvent = 0;
\r
8994 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8995 delayedTimerCallback();
\r
8997 delayedTimerCallback = cb;
\r
8998 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8999 (UINT) millisec, NULL);
\r
9002 DelayedEventCallback
\r
9005 if (delayedTimerEvent) {
\r
9006 return delayedTimerCallback;
\r
9013 CancelDelayedEvent()
\r
9015 if (delayedTimerEvent) {
\r
9016 KillTimer(hwndMain, delayedTimerEvent);
\r
9017 delayedTimerEvent = 0;
\r
9021 DWORD GetWin32Priority(int nice)
\r
9022 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
9024 REALTIME_PRIORITY_CLASS 0x00000100
\r
9025 HIGH_PRIORITY_CLASS 0x00000080
\r
9026 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
9027 NORMAL_PRIORITY_CLASS 0x00000020
\r
9028 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
9029 IDLE_PRIORITY_CLASS 0x00000040
\r
9031 if (nice < -15) return 0x00000080;
\r
9032 if (nice < 0) return 0x00008000;
\r
9033 if (nice == 0) return 0x00000020;
\r
9034 if (nice < 15) return 0x00004000;
\r
9035 return 0x00000040;
\r
9038 void RunCommand(char *cmdLine)
\r
9040 /* Now create the child process. */
\r
9041 STARTUPINFO siStartInfo;
\r
9042 PROCESS_INFORMATION piProcInfo;
\r
9044 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9045 siStartInfo.lpReserved = NULL;
\r
9046 siStartInfo.lpDesktop = NULL;
\r
9047 siStartInfo.lpTitle = NULL;
\r
9048 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9049 siStartInfo.cbReserved2 = 0;
\r
9050 siStartInfo.lpReserved2 = NULL;
\r
9051 siStartInfo.hStdInput = NULL;
\r
9052 siStartInfo.hStdOutput = NULL;
\r
9053 siStartInfo.hStdError = NULL;
\r
9055 CreateProcess(NULL,
\r
9056 cmdLine, /* command line */
\r
9057 NULL, /* process security attributes */
\r
9058 NULL, /* primary thread security attrs */
\r
9059 TRUE, /* handles are inherited */
\r
9060 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9061 NULL, /* use parent's environment */
\r
9063 &siStartInfo, /* STARTUPINFO pointer */
\r
9064 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9066 CloseHandle(piProcInfo.hThread);
\r
9069 /* Start a child process running the given program.
\r
9070 The process's standard output can be read from "from", and its
\r
9071 standard input can be written to "to".
\r
9072 Exit with fatal error if anything goes wrong.
\r
9073 Returns an opaque pointer that can be used to destroy the process
\r
9077 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
9079 #define BUFSIZE 4096
\r
9081 HANDLE hChildStdinRd, hChildStdinWr,
\r
9082 hChildStdoutRd, hChildStdoutWr;
\r
9083 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
9084 SECURITY_ATTRIBUTES saAttr;
\r
9086 PROCESS_INFORMATION piProcInfo;
\r
9087 STARTUPINFO siStartInfo;
\r
9089 char buf[MSG_SIZ];
\r
9092 if (appData.debugMode) {
\r
9093 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
9098 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
9099 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
9100 saAttr.bInheritHandle = TRUE;
\r
9101 saAttr.lpSecurityDescriptor = NULL;
\r
9104 * The steps for redirecting child's STDOUT:
\r
9105 * 1. Create anonymous pipe to be STDOUT for child.
\r
9106 * 2. Create a noninheritable duplicate of read handle,
\r
9107 * and close the inheritable read handle.
\r
9110 /* Create a pipe for the child's STDOUT. */
\r
9111 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
9112 return GetLastError();
\r
9115 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
9116 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
9117 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
9118 FALSE, /* not inherited */
\r
9119 DUPLICATE_SAME_ACCESS);
\r
9121 return GetLastError();
\r
9123 CloseHandle(hChildStdoutRd);
\r
9126 * The steps for redirecting child's STDIN:
\r
9127 * 1. Create anonymous pipe to be STDIN for child.
\r
9128 * 2. Create a noninheritable duplicate of write handle,
\r
9129 * and close the inheritable write handle.
\r
9132 /* Create a pipe for the child's STDIN. */
\r
9133 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
9134 return GetLastError();
\r
9137 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
9138 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
9139 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
9140 FALSE, /* not inherited */
\r
9141 DUPLICATE_SAME_ACCESS);
\r
9143 return GetLastError();
\r
9145 CloseHandle(hChildStdinWr);
\r
9147 /* Arrange to (1) look in dir for the child .exe file, and
\r
9148 * (2) have dir be the child's working directory. Interpret
\r
9149 * dir relative to the directory WinBoard loaded from. */
\r
9150 GetCurrentDirectory(MSG_SIZ, buf);
\r
9151 SetCurrentDirectory(installDir);
\r
9152 SetCurrentDirectory(dir);
\r
9154 /* Now create the child process. */
\r
9156 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9157 siStartInfo.lpReserved = NULL;
\r
9158 siStartInfo.lpDesktop = NULL;
\r
9159 siStartInfo.lpTitle = NULL;
\r
9160 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9161 siStartInfo.cbReserved2 = 0;
\r
9162 siStartInfo.lpReserved2 = NULL;
\r
9163 siStartInfo.hStdInput = hChildStdinRd;
\r
9164 siStartInfo.hStdOutput = hChildStdoutWr;
\r
9165 siStartInfo.hStdError = hChildStdoutWr;
\r
9167 fSuccess = CreateProcess(NULL,
\r
9168 cmdLine, /* command line */
\r
9169 NULL, /* process security attributes */
\r
9170 NULL, /* primary thread security attrs */
\r
9171 TRUE, /* handles are inherited */
\r
9172 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9173 NULL, /* use parent's environment */
\r
9175 &siStartInfo, /* STARTUPINFO pointer */
\r
9176 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9178 err = GetLastError();
\r
9179 SetCurrentDirectory(buf); /* return to prev directory */
\r
9184 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
9185 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
9186 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
9189 /* Close the handles we don't need in the parent */
\r
9190 CloseHandle(piProcInfo.hThread);
\r
9191 CloseHandle(hChildStdinRd);
\r
9192 CloseHandle(hChildStdoutWr);
\r
9194 /* Prepare return value */
\r
9195 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9196 cp->kind = CPReal;
\r
9197 cp->hProcess = piProcInfo.hProcess;
\r
9198 cp->pid = piProcInfo.dwProcessId;
\r
9199 cp->hFrom = hChildStdoutRdDup;
\r
9200 cp->hTo = hChildStdinWrDup;
\r
9202 *pr = (void *) cp;
\r
9204 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
9205 2000 where engines sometimes don't see the initial command(s)
\r
9206 from WinBoard and hang. I don't understand how that can happen,
\r
9207 but the Sleep is harmless, so I've put it in. Others have also
\r
9208 reported what may be the same problem, so hopefully this will fix
\r
9209 it for them too. */
\r
9217 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
9219 ChildProc *cp; int result;
\r
9221 cp = (ChildProc *) pr;
\r
9222 if (cp == NULL) return;
\r
9224 switch (cp->kind) {
\r
9226 /* TerminateProcess is considered harmful, so... */
\r
9227 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9228 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9229 /* The following doesn't work because the chess program
\r
9230 doesn't "have the same console" as WinBoard. Maybe
\r
9231 we could arrange for this even though neither WinBoard
\r
9232 nor the chess program uses a console for stdio? */
\r
9233 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9235 /* [AS] Special termination modes for misbehaving programs... */
\r
9236 if( signal & 8 ) {
\r
9237 result = TerminateProcess( cp->hProcess, 0 );
\r
9239 if ( appData.debugMode) {
\r
9240 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9243 else if( signal & 4 ) {
\r
9244 DWORD dw = WaitForSingleObject( cp->hProcess, appData.delayAfterQuit*1000 + 50 ); // Wait 3 seconds at most
\r
9246 if( dw != WAIT_OBJECT_0 ) {
\r
9247 result = TerminateProcess( cp->hProcess, 0 );
\r
9249 if ( appData.debugMode) {
\r
9250 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9256 CloseHandle(cp->hProcess);
\r
9260 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9264 closesocket(cp->sock);
\r
9269 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9270 closesocket(cp->sock);
\r
9271 closesocket(cp->sock2);
\r
9279 InterruptChildProcess(ProcRef pr)
\r
9283 cp = (ChildProc *) pr;
\r
9284 if (cp == NULL) return;
\r
9285 switch (cp->kind) {
\r
9287 /* The following doesn't work because the chess program
\r
9288 doesn't "have the same console" as WinBoard. Maybe
\r
9289 we could arrange for this even though neither WinBoard
\r
9290 nor the chess program uses a console for stdio */
\r
9291 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9296 /* Can't interrupt */
\r
9300 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9307 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9309 char cmdLine[MSG_SIZ];
\r
9311 if (port[0] == NULLCHAR) {
\r
9312 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9314 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9316 return StartChildProcess(cmdLine, "", pr);
\r
9320 /* Code to open TCP sockets */
\r
9323 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9329 struct sockaddr_in sa, mysa;
\r
9330 struct hostent FAR *hp;
\r
9331 unsigned short uport;
\r
9332 WORD wVersionRequested;
\r
9335 /* Initialize socket DLL */
\r
9336 wVersionRequested = MAKEWORD(1, 1);
\r
9337 err = WSAStartup(wVersionRequested, &wsaData);
\r
9338 if (err != 0) return err;
\r
9341 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9342 err = WSAGetLastError();
\r
9347 /* Bind local address using (mostly) don't-care values.
\r
9349 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9350 mysa.sin_family = AF_INET;
\r
9351 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9352 uport = (unsigned short) 0;
\r
9353 mysa.sin_port = htons(uport);
\r
9354 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9355 == SOCKET_ERROR) {
\r
9356 err = WSAGetLastError();
\r
9361 /* Resolve remote host name */
\r
9362 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9363 if (!(hp = gethostbyname(host))) {
\r
9364 unsigned int b0, b1, b2, b3;
\r
9366 err = WSAGetLastError();
\r
9368 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9369 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9370 hp->h_addrtype = AF_INET;
\r
9372 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9373 hp->h_addr_list[0] = (char *) malloc(4);
\r
9374 hp->h_addr_list[0][0] = (char) b0;
\r
9375 hp->h_addr_list[0][1] = (char) b1;
\r
9376 hp->h_addr_list[0][2] = (char) b2;
\r
9377 hp->h_addr_list[0][3] = (char) b3;
\r
9383 sa.sin_family = hp->h_addrtype;
\r
9384 uport = (unsigned short) atoi(port);
\r
9385 sa.sin_port = htons(uport);
\r
9386 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9388 /* Make connection */
\r
9389 if (connect(s, (struct sockaddr *) &sa,
\r
9390 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9391 err = WSAGetLastError();
\r
9396 /* Prepare return value */
\r
9397 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9398 cp->kind = CPSock;
\r
9400 *pr = (ProcRef *) cp;
\r
9406 OpenCommPort(char *name, ProcRef *pr)
\r
9411 char fullname[MSG_SIZ];
\r
9413 if (*name != '\\')
\r
9414 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9416 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9418 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9419 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9420 if (h == (HANDLE) -1) {
\r
9421 return GetLastError();
\r
9425 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9427 /* Accumulate characters until a 100ms pause, then parse */
\r
9428 ct.ReadIntervalTimeout = 100;
\r
9429 ct.ReadTotalTimeoutMultiplier = 0;
\r
9430 ct.ReadTotalTimeoutConstant = 0;
\r
9431 ct.WriteTotalTimeoutMultiplier = 0;
\r
9432 ct.WriteTotalTimeoutConstant = 0;
\r
9433 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9435 /* Prepare return value */
\r
9436 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9437 cp->kind = CPComm;
\r
9440 *pr = (ProcRef *) cp;
\r
9446 OpenLoopback(ProcRef *pr)
\r
9448 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9454 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9459 struct sockaddr_in sa, mysa;
\r
9460 struct hostent FAR *hp;
\r
9461 unsigned short uport;
\r
9462 WORD wVersionRequested;
\r
9465 char stderrPortStr[MSG_SIZ];
\r
9467 /* Initialize socket DLL */
\r
9468 wVersionRequested = MAKEWORD(1, 1);
\r
9469 err = WSAStartup(wVersionRequested, &wsaData);
\r
9470 if (err != 0) return err;
\r
9472 /* Resolve remote host name */
\r
9473 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9474 if (!(hp = gethostbyname(host))) {
\r
9475 unsigned int b0, b1, b2, b3;
\r
9477 err = WSAGetLastError();
\r
9479 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9480 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9481 hp->h_addrtype = AF_INET;
\r
9483 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9484 hp->h_addr_list[0] = (char *) malloc(4);
\r
9485 hp->h_addr_list[0][0] = (char) b0;
\r
9486 hp->h_addr_list[0][1] = (char) b1;
\r
9487 hp->h_addr_list[0][2] = (char) b2;
\r
9488 hp->h_addr_list[0][3] = (char) b3;
\r
9494 sa.sin_family = hp->h_addrtype;
\r
9495 uport = (unsigned short) 514;
\r
9496 sa.sin_port = htons(uport);
\r
9497 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9499 /* Bind local socket to unused "privileged" port address
\r
9501 s = INVALID_SOCKET;
\r
9502 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9503 mysa.sin_family = AF_INET;
\r
9504 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9505 for (fromPort = 1023;; fromPort--) {
\r
9506 if (fromPort < 0) {
\r
9508 return WSAEADDRINUSE;
\r
9510 if (s == INVALID_SOCKET) {
\r
9511 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9512 err = WSAGetLastError();
\r
9517 uport = (unsigned short) fromPort;
\r
9518 mysa.sin_port = htons(uport);
\r
9519 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9520 == SOCKET_ERROR) {
\r
9521 err = WSAGetLastError();
\r
9522 if (err == WSAEADDRINUSE) continue;
\r
9526 if (connect(s, (struct sockaddr *) &sa,
\r
9527 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9528 err = WSAGetLastError();
\r
9529 if (err == WSAEADDRINUSE) {
\r
9540 /* Bind stderr local socket to unused "privileged" port address
\r
9542 s2 = INVALID_SOCKET;
\r
9543 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9544 mysa.sin_family = AF_INET;
\r
9545 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9546 for (fromPort = 1023;; fromPort--) {
\r
9547 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9548 if (fromPort < 0) {
\r
9549 (void) closesocket(s);
\r
9551 return WSAEADDRINUSE;
\r
9553 if (s2 == INVALID_SOCKET) {
\r
9554 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9555 err = WSAGetLastError();
\r
9561 uport = (unsigned short) fromPort;
\r
9562 mysa.sin_port = htons(uport);
\r
9563 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9564 == SOCKET_ERROR) {
\r
9565 err = WSAGetLastError();
\r
9566 if (err == WSAEADDRINUSE) continue;
\r
9567 (void) closesocket(s);
\r
9571 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9572 err = WSAGetLastError();
\r
9573 if (err == WSAEADDRINUSE) {
\r
9575 s2 = INVALID_SOCKET;
\r
9578 (void) closesocket(s);
\r
9579 (void) closesocket(s2);
\r
9585 prevStderrPort = fromPort; // remember port used
\r
9586 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9588 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9589 err = WSAGetLastError();
\r
9590 (void) closesocket(s);
\r
9591 (void) closesocket(s2);
\r
9596 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9597 err = WSAGetLastError();
\r
9598 (void) closesocket(s);
\r
9599 (void) closesocket(s2);
\r
9603 if (*user == NULLCHAR) user = UserName();
\r
9604 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9605 err = WSAGetLastError();
\r
9606 (void) closesocket(s);
\r
9607 (void) closesocket(s2);
\r
9611 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9612 err = WSAGetLastError();
\r
9613 (void) closesocket(s);
\r
9614 (void) closesocket(s2);
\r
9619 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9620 err = WSAGetLastError();
\r
9621 (void) closesocket(s);
\r
9622 (void) closesocket(s2);
\r
9626 (void) closesocket(s2); /* Stop listening */
\r
9628 /* Prepare return value */
\r
9629 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9630 cp->kind = CPRcmd;
\r
9633 *pr = (ProcRef *) cp;
\r
9640 AddInputSource(ProcRef pr, int lineByLine,
\r
9641 InputCallback func, VOIDSTAR closure)
\r
9643 InputSource *is, *is2 = NULL;
\r
9644 ChildProc *cp = (ChildProc *) pr;
\r
9646 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9647 is->lineByLine = lineByLine;
\r
9649 is->closure = closure;
\r
9650 is->second = NULL;
\r
9651 is->next = is->buf;
\r
9652 if (pr == NoProc) {
\r
9653 is->kind = CPReal;
\r
9654 consoleInputSource = is;
\r
9656 is->kind = cp->kind;
\r
9658 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9659 we create all threads suspended so that the is->hThread variable can be
\r
9660 safely assigned, then let the threads start with ResumeThread.
\r
9662 switch (cp->kind) {
\r
9664 is->hFile = cp->hFrom;
\r
9665 cp->hFrom = NULL; /* now owned by InputThread */
\r
9667 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9668 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9672 is->hFile = cp->hFrom;
\r
9673 cp->hFrom = NULL; /* now owned by InputThread */
\r
9675 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9676 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9680 is->sock = cp->sock;
\r
9682 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9683 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9687 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9689 is->sock = cp->sock;
\r
9691 is2->sock = cp->sock2;
\r
9692 is2->second = is2;
\r
9694 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9695 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9697 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9698 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9702 if( is->hThread != NULL ) {
\r
9703 ResumeThread( is->hThread );
\r
9706 if( is2 != NULL && is2->hThread != NULL ) {
\r
9707 ResumeThread( is2->hThread );
\r
9711 return (InputSourceRef) is;
\r
9715 RemoveInputSource(InputSourceRef isr)
\r
9719 is = (InputSource *) isr;
\r
9720 is->hThread = NULL; /* tell thread to stop */
\r
9721 CloseHandle(is->hThread);
\r
9722 if (is->second != NULL) {
\r
9723 is->second->hThread = NULL;
\r
9724 CloseHandle(is->second->hThread);
\r
9728 int no_wrap(char *message, int count)
\r
9730 ConsoleOutput(message, count, FALSE);
\r
9735 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9738 int outCount = SOCKET_ERROR;
\r
9739 ChildProc *cp = (ChildProc *) pr;
\r
9740 static OVERLAPPED ovl;
\r
9741 static int line = 0;
\r
9745 if (appData.noJoin || !appData.useInternalWrap)
\r
9746 return no_wrap(message, count);
\r
9749 int width = get_term_width();
\r
9750 int len = wrap(NULL, message, count, width, &line);
\r
9751 char *msg = malloc(len);
\r
9755 return no_wrap(message, count);
\r
9758 dbgchk = wrap(msg, message, count, width, &line);
\r
9759 if (dbgchk != len && appData.debugMode)
\r
9760 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9761 ConsoleOutput(msg, len, FALSE);
\r
9768 if (ovl.hEvent == NULL) {
\r
9769 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9771 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9773 switch (cp->kind) {
\r
9776 outCount = send(cp->sock, message, count, 0);
\r
9777 if (outCount == SOCKET_ERROR) {
\r
9778 *outError = WSAGetLastError();
\r
9780 *outError = NO_ERROR;
\r
9785 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9786 &dOutCount, NULL)) {
\r
9787 *outError = NO_ERROR;
\r
9788 outCount = (int) dOutCount;
\r
9790 *outError = GetLastError();
\r
9795 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9796 &dOutCount, &ovl);
\r
9797 if (*outError == NO_ERROR) {
\r
9798 outCount = (int) dOutCount;
\r
9808 if(n != 0) Sleep(n);
\r
9812 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9815 /* Ignore delay, not implemented for WinBoard */
\r
9816 return OutputToProcess(pr, message, count, outError);
\r
9821 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9822 char *buf, int count, int error)
\r
9824 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9827 /* see wgamelist.c for Game List functions */
\r
9828 /* see wedittags.c for Edit Tags functions */
\r
9835 char buf[MSG_SIZ];
\r
9838 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9839 f = fopen(buf, "r");
\r
9841 ProcessICSInitScript(f);
\r
9851 StartAnalysisClock()
\r
9853 if (analysisTimerEvent) return;
\r
9854 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9855 (UINT) 2000, NULL);
\r
9859 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9861 highlightInfo.sq[0].x = fromX;
\r
9862 highlightInfo.sq[0].y = fromY;
\r
9863 highlightInfo.sq[1].x = toX;
\r
9864 highlightInfo.sq[1].y = toY;
\r
9870 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9871 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9875 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9877 premoveHighlightInfo.sq[0].x = fromX;
\r
9878 premoveHighlightInfo.sq[0].y = fromY;
\r
9879 premoveHighlightInfo.sq[1].x = toX;
\r
9880 premoveHighlightInfo.sq[1].y = toY;
\r
9884 ClearPremoveHighlights()
\r
9886 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9887 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9891 ShutDownFrontEnd()
\r
9893 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9894 DeleteClipboardTempFiles();
\r
9900 if (IsIconic(hwndMain))
\r
9901 ShowWindow(hwndMain, SW_RESTORE);
\r
9903 SetActiveWindow(hwndMain);
\r
9907 * Prototypes for animation support routines
\r
9909 static void ScreenSquare(int column, int row, POINT * pt);
\r
9910 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9911 POINT frames[], int * nFrames);
\r
9917 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9918 { // [HGM] atomic: animate blast wave
\r
9921 explodeInfo.fromX = fromX;
\r
9922 explodeInfo.fromY = fromY;
\r
9923 explodeInfo.toX = toX;
\r
9924 explodeInfo.toY = toY;
\r
9925 for(i=1; i<4*kFactor; i++) {
\r
9926 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9927 DrawPosition(FALSE, board);
\r
9928 Sleep(appData.animSpeed);
\r
9930 explodeInfo.radius = 0;
\r
9931 DrawPosition(TRUE, board);
\r
9935 AnimateMove(board, fromX, fromY, toX, toY)
\r
9942 ChessSquare piece;
\r
9943 int x = toX, y = toY;
\r
9944 POINT start, finish, mid;
\r
9945 POINT frames[kFactor * 2 + 1];
\r
9948 if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();
\r
9950 if (!appData.animate) return;
\r
9951 if (doingSizing) return;
\r
9952 if (fromY < 0 || fromX < 0) return;
\r
9953 piece = board[fromY][fromX];
\r
9954 if (piece >= EmptySquare) return;
\r
9956 if(killX >= 0) toX = killX, toY = killY; // [HGM] lion: first to kill square
\r
9960 ScreenSquare(fromX, fromY, &start);
\r
9961 ScreenSquare(toX, toY, &finish);
\r
9963 /* All moves except knight jumps move in straight line */
\r
9964 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9965 mid.x = start.x + (finish.x - start.x) / 2;
\r
9966 mid.y = start.y + (finish.y - start.y) / 2;
\r
9968 /* Knight: make straight movement then diagonal */
\r
9969 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9970 mid.x = start.x + (finish.x - start.x) / 2;
\r
9974 mid.y = start.y + (finish.y - start.y) / 2;
\r
9978 /* Don't use as many frames for very short moves */
\r
9979 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9980 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9982 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9984 animInfo.from.x = fromX;
\r
9985 animInfo.from.y = fromY;
\r
9986 animInfo.to.x = toX;
\r
9987 animInfo.to.y = toY;
\r
9988 animInfo.lastpos = start;
\r
9989 animInfo.piece = piece;
\r
9990 for (n = 0; n < nFrames; n++) {
\r
9991 animInfo.pos = frames[n];
\r
9992 DrawPosition(FALSE, NULL);
\r
9993 animInfo.lastpos = animInfo.pos;
\r
9994 Sleep(appData.animSpeed);
\r
9996 animInfo.pos = finish;
\r
9997 DrawPosition(FALSE, NULL);
\r
9999 if(toX != x || toY != y) { fromX = toX; fromY = toY; toX = x; toY = y; goto again; } // second leg
\r
10001 animInfo.piece = EmptySquare;
\r
10002 Explode(board, fromX, fromY, toX, toY);
\r
10005 /* Convert board position to corner of screen rect and color */
\r
10008 ScreenSquare(column, row, pt)
\r
10009 int column; int row; POINT * pt;
\r
10012 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
10013 pt->y = lineGap + row * (squareSize + lineGap) + border;
\r
10015 pt->x = lineGap + column * (squareSize + lineGap) + border;
\r
10016 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
10020 /* Generate a series of frame coords from start->mid->finish.
\r
10021 The movement rate doubles until the half way point is
\r
10022 reached, then halves back down to the final destination,
\r
10023 which gives a nice slow in/out effect. The algorithmn
\r
10024 may seem to generate too many intermediates for short
\r
10025 moves, but remember that the purpose is to attract the
\r
10026 viewers attention to the piece about to be moved and
\r
10027 then to where it ends up. Too few frames would be less
\r
10031 Tween(start, mid, finish, factor, frames, nFrames)
\r
10032 POINT * start; POINT * mid;
\r
10033 POINT * finish; int factor;
\r
10034 POINT frames[]; int * nFrames;
\r
10036 int n, fraction = 1, count = 0;
\r
10038 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
10039 for (n = 0; n < factor; n++)
\r
10041 for (n = 0; n < factor; n++) {
\r
10042 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
10043 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
10045 fraction = fraction / 2;
\r
10049 frames[count] = *mid;
\r
10052 /* Slow out, stepping 1/2, then 1/4, ... */
\r
10054 for (n = 0; n < factor; n++) {
\r
10055 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
10056 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
10058 fraction = fraction * 2;
\r
10060 *nFrames = count;
\r
10064 SettingsPopUp(ChessProgramState *cps)
\r
10065 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
10066 EngineOptionsPopup(savedHwnd, cps);
\r
10069 int flock(int fid, int code)
\r
10071 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
10073 ov.hEvent = NULL;
\r
10075 ov.OffsetHigh = 0;
\r
10077 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
10079 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
10080 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
10081 default: return -1;
\r
10090 static char col[8][20];
\r
10091 COLORREF color = *(COLORREF *) colorVariable[n];
\r
10093 snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
10098 ActivateTheme (int new)
\r
10099 { // Redo initialization of features depending on options that can occur in themes
\r
10101 if(new) InitDrawingColors();
\r
10102 fontBitmapSquareSize = 0; // request creation of new font pieces
\r
10103 InitDrawingSizes(boardSize, 0);
\r
10104 InvalidateRect(hwndMain, NULL, TRUE);
\r