2 * WinBoard.c -- Windows NT front end to XBoard
\r
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
\r
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
\r
8 * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 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;
\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, 2, 0, 0 },
\r
538 { "teeny", 25, 1, 1, 2, 0, 0 },
\r
539 { "dinky", 29, 1, 1, 2, 0, 0 },
\r
540 { "petite", 33, 1, 1, 2, 0, 0 },
\r
541 { "slim", 37, 2, 1, 1, 0, 0 },
\r
542 { "small", 40, 2, 1, 1, 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 == 2 ? 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[3][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_("&Fil"), N_("&Ed"), N_("&Vw"), N_("&Mod"), N_("&Act"), N_("E&ng"), N_("&Opt"), N_("&Hlp"), NULL },
\r
607 { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },
\r
611 MySound sounds[(int)NSoundClasses];
\r
612 MyTextAttribs textAttribs[(int)NColorClasses];
\r
614 MyColorizeAttribs colorizeAttribs[] = {
\r
615 { (COLORREF)0, 0, N_("Shout Text") },
\r
616 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
617 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
618 { (COLORREF)0, 0, N_("Channel Text") },
\r
619 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
620 { (COLORREF)0, 0, N_("Tell Text") },
\r
621 { (COLORREF)0, 0, N_("Challenge Text") },
\r
622 { (COLORREF)0, 0, N_("Request Text") },
\r
623 { (COLORREF)0, 0, N_("Seek Text") },
\r
624 { (COLORREF)0, 0, N_("Normal Text") },
\r
625 { (COLORREF)0, 0, N_("None") }
\r
630 static char *commentTitle;
\r
631 static char *commentText;
\r
632 static int commentIndex;
\r
633 static Boolean editComment = FALSE;
\r
636 char errorTitle[MSG_SIZ];
\r
637 char errorMessage[2*MSG_SIZ];
\r
638 HWND errorDialog = NULL;
\r
639 BOOLEAN moveErrorMessageUp = FALSE;
\r
640 BOOLEAN consoleEcho = TRUE;
\r
641 CHARFORMAT consoleCF;
\r
642 COLORREF consoleBackgroundColor;
\r
644 char *programVersion;
\r
650 typedef int CPKind;
\r
659 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
662 #define INPUT_SOURCE_BUF_SIZE 4096
\r
664 typedef struct _InputSource {
\r
671 char buf[INPUT_SOURCE_BUF_SIZE];
\r
675 InputCallback func;
\r
676 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
680 InputSource *consoleInputSource;
\r
685 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
686 VOID ConsoleCreate();
\r
688 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
689 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
690 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
691 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
693 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
694 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
695 void ParseIcsTextMenu(char *icsTextMenuString);
\r
696 VOID PopUpNameDialog(char firstchar);
\r
697 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
701 int GameListOptions();
\r
703 int dummy; // [HGM] for obsolete args
\r
705 HWND hwndMain = NULL; /* root window*/
\r
706 HWND hwndConsole = NULL;
\r
707 HWND commentDialog = NULL;
\r
708 HWND moveHistoryDialog = NULL;
\r
709 HWND evalGraphDialog = NULL;
\r
710 HWND engineOutputDialog = NULL;
\r
711 HWND gameListDialog = NULL;
\r
712 HWND editTagsDialog = NULL;
\r
714 int commentUp = FALSE;
\r
716 WindowPlacement wpMain;
\r
717 WindowPlacement wpConsole;
\r
718 WindowPlacement wpComment;
\r
719 WindowPlacement wpMoveHistory;
\r
720 WindowPlacement wpEvalGraph;
\r
721 WindowPlacement wpEngineOutput;
\r
722 WindowPlacement wpGameList;
\r
723 WindowPlacement wpTags;
\r
725 VOID EngineOptionsPopup(); // [HGM] settings
\r
727 VOID GothicPopUp(char *title, VariantClass variant);
\r
729 * Setting "frozen" should disable all user input other than deleting
\r
730 * the window. We do this while engines are initializing themselves.
\r
732 static int frozen = 0;
\r
733 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
739 if (frozen) return;
\r
741 hmenu = GetMenu(hwndMain);
\r
742 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
743 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
745 DrawMenuBar(hwndMain);
\r
748 /* Undo a FreezeUI */
\r
754 if (!frozen) return;
\r
756 hmenu = GetMenu(hwndMain);
\r
757 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
758 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
760 DrawMenuBar(hwndMain);
\r
763 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
765 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
771 #define JAWS_ALT_INTERCEPT
\r
772 #define JAWS_KBUP_NAVIGATION
\r
773 #define JAWS_KBDOWN_NAVIGATION
\r
774 #define JAWS_MENU_ITEMS
\r
775 #define JAWS_SILENCE
\r
776 #define JAWS_REPLAY
\r
778 #define JAWS_COPYRIGHT
\r
779 #define JAWS_DELETE(X) X
\r
780 #define SAYMACHINEMOVE()
\r
784 /*---------------------------------------------------------------------------*\
\r
788 \*---------------------------------------------------------------------------*/
\r
790 static void HandleMessage P((MSG *message));
\r
791 static HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
794 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
795 LPSTR lpCmdLine, int nCmdShow)
\r
798 // INITCOMMONCONTROLSEX ex;
\r
802 LoadLibrary("RICHED32.DLL");
\r
803 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
805 if (!InitApplication(hInstance)) {
\r
808 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
815 // InitCommonControlsEx(&ex);
\r
816 InitCommonControls();
\r
818 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
819 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
820 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
822 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
824 while (GetMessage(&msg, /* message structure */
\r
825 NULL, /* handle of window receiving the message */
\r
826 0, /* lowest message to examine */
\r
827 0)) /* highest message to examine */
\r
829 HandleMessage(&msg);
\r
833 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
837 HandleMessage (MSG *message)
\r
839 MSG msg = *message;
\r
841 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
842 // [HGM] navigate: switch between all windows with tab
\r
843 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
844 int i, currentElement = 0;
\r
846 // first determine what element of the chain we come from (if any)
\r
847 if(appData.icsActive) {
\r
848 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
849 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
851 if(engineOutputDialog && EngineOutputIsUp()) {
\r
852 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
853 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
855 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
856 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
858 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
859 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
860 if(msg.hwnd == e1) currentElement = 2; else
\r
861 if(msg.hwnd == e2) currentElement = 3; else
\r
862 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
863 if(msg.hwnd == mh) currentElement = 4; else
\r
864 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
865 if(msg.hwnd == hText) currentElement = 5; else
\r
866 if(msg.hwnd == hInput) currentElement = 6; else
\r
867 for (i = 0; i < N_BUTTONS; i++) {
\r
868 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
871 // determine where to go to
\r
872 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
874 currentElement = (currentElement + direction) % 7;
\r
875 switch(currentElement) {
\r
877 h = hwndMain; break; // passing this case always makes the loop exit
\r
879 h = buttonDesc[0].hwnd; break; // could be NULL
\r
881 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
884 if(!EngineOutputIsUp()) continue;
\r
887 if(!MoveHistoryIsUp()) continue;
\r
889 // case 6: // input to eval graph does not seem to get here!
\r
890 // if(!EvalGraphIsUp()) continue;
\r
891 // h = evalGraphDialog; break;
\r
893 if(!appData.icsActive) continue;
\r
897 if(!appData.icsActive) continue;
\r
903 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
904 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
907 return; // this message now has been processed
\r
911 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
912 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
913 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
914 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
915 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
916 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
917 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
918 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
919 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
920 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
921 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
922 for(i=0; i<MAX_CHAT; i++)
\r
923 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
926 if(done) return; // [HGM] chat: end patch
\r
927 TranslateMessage(&msg); /* Translates virtual key codes */
\r
928 DispatchMessage(&msg); /* Dispatches message to window */
\r
934 { /* Dispatch pending messages */
\r
936 while (PeekMessage(&msg, /* message structure */
\r
937 NULL, /* handle of window receiving the message */
\r
938 0, /* lowest message to examine */
\r
939 0, /* highest message to examine */
\r
942 HandleMessage(&msg);
\r
946 /*---------------------------------------------------------------------------*\
\r
948 * Initialization functions
\r
950 \*---------------------------------------------------------------------------*/
\r
954 { // update user logo if necessary
\r
955 static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;
\r
957 if(appData.autoLogo) {
\r
958 curName = UserName();
\r
959 if(strcmp(curName, oldUserName)) {
\r
960 GetCurrentDirectory(MSG_SIZ, dir);
\r
961 SetCurrentDirectory(installDir);
\r
962 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
963 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
964 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
965 if(userLogo == NULL)
\r
966 userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
967 SetCurrentDirectory(dir); /* return to prev directory */
\r
973 InitApplication(HINSTANCE hInstance)
\r
977 /* Fill in window class structure with parameters that describe the */
\r
980 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
981 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
982 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
983 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
984 wc.hInstance = hInstance; /* Owner of this class */
\r
985 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
986 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
987 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
988 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
989 wc.lpszClassName = szAppName; /* Name to register as */
\r
991 /* Register the window class and return success/failure code. */
\r
992 if (!RegisterClass(&wc)) return FALSE;
\r
994 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
995 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
997 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
998 wc.hInstance = hInstance;
\r
999 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
1000 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
1001 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
1002 wc.lpszMenuName = NULL;
\r
1003 wc.lpszClassName = szConsoleName;
\r
1005 if (!RegisterClass(&wc)) return FALSE;
\r
1010 /* Set by InitInstance, used by EnsureOnScreen */
\r
1011 int screenHeight, screenWidth;
\r
1012 RECT screenGeometry;
\r
1015 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
1017 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
1018 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
1019 if (*x > screenGeometry.right - 32) *x = screenGeometry.left;
\r
1020 if (*y > screenGeometry.bottom - 32) *y = screenGeometry.top;
\r
1021 if (*x < screenGeometry.left + minX) *x = screenGeometry.left + minX;
\r
1022 if (*y < screenGeometry.top + minY) *y = screenGeometry.top + minY;
\r
1026 LoadLogo(ChessProgramState *cps, int n, Boolean ics)
\r
1028 char buf[MSG_SIZ], dir[MSG_SIZ];
\r
1029 GetCurrentDirectory(MSG_SIZ, dir);
\r
1030 SetCurrentDirectory(installDir);
\r
1031 if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {
\r
1032 cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1034 if (cps->programLogo == NULL && appData.debugMode) {
\r
1035 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );
\r
1037 } else if(appData.autoLogo) {
\r
1038 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
\r
1039 char *opponent = "";
\r
1040 if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;
\r
1041 if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;
\r
1042 sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);
\r
1043 if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {
\r
1044 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
1045 cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1048 if(appData.directory[n] && appData.directory[n][0]) {
\r
1049 SetCurrentDirectory(appData.directory[n]);
\r
1050 cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1053 SetCurrentDirectory(dir); /* return to prev directory */
\r
1059 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
1060 backTextureSquareSize = 0; // kludge to force recalculation of texturemode
\r
1062 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1063 if(liteBackTexture) DeleteObject(liteBackTexture);
\r
1064 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1065 liteBackTextureMode = appData.liteBackTextureMode;
\r
1067 if (liteBackTexture == NULL && appData.debugMode) {
\r
1068 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1072 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1073 if(darkBackTexture) DeleteObject(darkBackTexture);
\r
1074 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1075 darkBackTextureMode = appData.darkBackTextureMode;
\r
1077 if (darkBackTexture == NULL && appData.debugMode) {
\r
1078 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1083 #ifndef SM_CXVIRTUALSCREEN
\r
1084 #define SM_CXVIRTUALSCREEN 78
\r
1086 #ifndef SM_CYVIRTUALSCREEN
\r
1087 #define SM_CYVIRTUALSCREEN 79
\r
1089 #ifndef SM_XVIRTUALSCREEN
\r
1090 #define SM_XVIRTUALSCREEN 76
\r
1092 #ifndef SM_YVIRTUALSCREEN
\r
1093 #define SM_YVIRTUALSCREEN 77
\r
1099 screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
\r
1100 if( !screenHeight ) screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1101 screenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
\r
1102 if( !screenWidth ) screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1103 screenGeometry.left = GetSystemMetrics(SM_XVIRTUALSCREEN);
\r
1104 screenGeometry.top = GetSystemMetrics(SM_YVIRTUALSCREEN);
\r
1105 screenGeometry.right = screenGeometry.left + screenWidth;
\r
1106 screenGeometry.bottom = screenGeometry.top + screenHeight;
\r
1109 ChessProgramState broadcast;
\r
1112 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
1114 HWND hwnd; /* Main window handle. */
\r
1116 WINDOWPLACEMENT wp;
\r
1119 hInst = hInstance; /* Store instance handle in our global variable */
\r
1120 programName = szAppName;
\r
1122 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
1123 *filepart = NULLCHAR;
\r
1124 SetCurrentDirectory(installDir);
\r
1126 GetCurrentDirectory(MSG_SIZ, installDir);
\r
1128 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
1130 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
1131 /* xboard, and older WinBoards, controlled the move sound with the
\r
1132 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
1133 always turn the option on (so that the backend will call us),
\r
1134 then let the user turn the sound off by setting it to silence if
\r
1135 desired. To accommodate old winboard.ini files saved by old
\r
1136 versions of WinBoard, we also turn off the sound if the option
\r
1137 was initially set to false. [HGM] taken out of InitAppData */
\r
1138 if (!appData.ringBellAfterMoves) {
\r
1139 sounds[(int)SoundMove].name = strdup("");
\r
1140 appData.ringBellAfterMoves = TRUE;
\r
1142 if (appData.debugMode) {
\r
1143 char *c = appData.nameOfDebugFile;
\r
1144 if(strstr(c, "///") == c) {
\r
1145 broadcast.which = "broadcaster";
\r
1146 broadcast.pr = NoProc;
\r
1147 broadcast.isr = NULL;
\r
1148 broadcast.prog = c + 3;
\r
1149 broadcast.dir = ".";
\r
1150 broadcast.host = "localhost";
\r
1151 StartChessProgram(&broadcast);
\r
1152 debugFP = (FILE*) _fdopen(_open_osfhandle((long)(((ChildProc*)(broadcast.pr))->hTo), _O_WRONLY), "w");
\r
1154 debugFP = fopen(c, "w");
\r
1155 setbuf(debugFP, NULL);
\r
1158 LoadLanguageFile(appData.language);
\r
1162 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
1163 // InitEngineUCI( installDir, &second );
\r
1165 /* Create a main window for this application instance. */
\r
1166 hwnd = CreateWindow(szAppName, szTitle,
\r
1167 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
1168 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
1169 NULL, NULL, hInstance, NULL);
\r
1172 /* If window could not be created, return "failure" */
\r
1177 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1178 LoadLogo(&first, 0, FALSE);
\r
1179 LoadLogo(&second, 1, appData.icsActive);
\r
1183 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1184 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1185 iconCurrent = iconWhite;
\r
1186 InitDrawingColors();
\r
1188 InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args
\r
1189 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1190 /* Compute window size for each board size, and use the largest
\r
1191 size that fits on this screen as the default. */
\r
1192 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1193 if (boardSize == (BoardSize)-1 &&
\r
1194 winH <= screenHeight
\r
1195 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1196 && winW <= screenWidth) {
\r
1197 boardSize = (BoardSize)ibs;
\r
1201 InitDrawingSizes(boardSize, 0);
\r
1202 RecentEngineMenu(appData.recentEngineList);
\r
1204 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1206 /* [AS] Load textures if specified */
\r
1209 mysrandom( (unsigned) time(NULL) );
\r
1211 /* [AS] Restore layout */
\r
1212 if( wpMoveHistory.visible ) {
\r
1213 MoveHistoryPopUp();
\r
1216 if( wpEvalGraph.visible ) {
\r
1220 if( wpEngineOutput.visible ) {
\r
1221 EngineOutputPopUp();
\r
1224 /* Make the window visible; update its client area; and return "success" */
\r
1225 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1226 wp.length = sizeof(WINDOWPLACEMENT);
\r
1228 wp.showCmd = nCmdShow;
\r
1229 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1230 wp.rcNormalPosition.left = wpMain.x;
\r
1231 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1232 wp.rcNormalPosition.top = wpMain.y;
\r
1233 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1234 SetWindowPlacement(hwndMain, &wp);
\r
1236 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1238 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1239 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1241 if (hwndConsole) {
\r
1243 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1244 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1246 ShowWindow(hwndConsole, nCmdShow);
\r
1247 SetActiveWindow(hwndConsole);
\r
1249 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1250 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1259 HMENU hmenu = GetMenu(hwndMain);
\r
1261 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1262 MF_BYCOMMAND|((appData.icsActive &&
\r
1263 *appData.icsCommPort != NULLCHAR) ?
\r
1264 MF_ENABLED : MF_GRAYED));
\r
1265 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1266 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1267 MF_CHECKED : MF_UNCHECKED));
\r
1268 EnableMenuItem(hmenu, IDM_SaveSelected, MF_GRAYED);
\r
1271 //---------------------------------------------------------------------------------------------------------
\r
1273 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1274 #define XBOARD FALSE
\r
1276 #define OPTCHAR "/"
\r
1277 #define SEPCHAR "="
\r
1278 #define TOPLEVEL 0
\r
1282 // front-end part of option handling
\r
1285 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1287 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1288 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1291 lf->lfEscapement = 0;
\r
1292 lf->lfOrientation = 0;
\r
1293 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1294 lf->lfItalic = mfp->italic;
\r
1295 lf->lfUnderline = mfp->underline;
\r
1296 lf->lfStrikeOut = mfp->strikeout;
\r
1297 lf->lfCharSet = mfp->charset;
\r
1298 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1302 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1303 lf->lfQuality = DEFAULT_QUALITY;
\r
1304 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1305 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1309 CreateFontInMF(MyFont *mf)
\r
1311 LFfromMFP(&mf->lf, &mf->mfp);
\r
1312 if (mf->hf) DeleteObject(mf->hf);
\r
1313 mf->hf = CreateFontIndirect(&mf->lf);
\r
1316 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1318 colorVariable[] = {
\r
1319 &whitePieceColor,
\r
1320 &blackPieceColor,
\r
1321 &lightSquareColor,
\r
1322 &darkSquareColor,
\r
1323 &highlightSquareColor,
\r
1324 &premoveHighlightColor,
\r
1326 &consoleBackgroundColor,
\r
1327 &appData.fontForeColorWhite,
\r
1328 &appData.fontBackColorWhite,
\r
1329 &appData.fontForeColorBlack,
\r
1330 &appData.fontBackColorBlack,
\r
1331 &appData.evalHistColorWhite,
\r
1332 &appData.evalHistColorBlack,
\r
1333 &appData.highlightArrowColor,
\r
1336 /* Command line font name parser. NULL name means do nothing.
\r
1337 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1338 For backward compatibility, syntax without the colon is also
\r
1339 accepted, but font names with digits in them won't work in that case.
\r
1342 ParseFontName(char *name, MyFontParams *mfp)
\r
1345 if (name == NULL) return;
\r
1347 q = strchr(p, ':');
\r
1349 if (q - p >= sizeof(mfp->faceName))
\r
1350 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1351 memcpy(mfp->faceName, p, q - p);
\r
1352 mfp->faceName[q - p] = NULLCHAR;
\r
1355 q = mfp->faceName;
\r
1357 while (*p && !isdigit(*p)) {
\r
1359 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1360 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1362 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1365 if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);
\r
1366 mfp->pointSize = (float) atof(p);
\r
1367 mfp->bold = (strchr(p, 'b') != NULL);
\r
1368 mfp->italic = (strchr(p, 'i') != NULL);
\r
1369 mfp->underline = (strchr(p, 'u') != NULL);
\r
1370 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1371 mfp->charset = DEFAULT_CHARSET;
\r
1372 q = strchr(p, 'c');
\r
1374 mfp->charset = (BYTE) atoi(q+1);
\r
1378 ParseFont(char *name, int number)
\r
1379 { // wrapper to shield back-end from 'font'
\r
1380 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1385 { // in WB we have a 2D array of fonts; this initializes their description
\r
1387 /* Point font array elements to structures and
\r
1388 parse default font names */
\r
1389 for (i=0; i<NUM_FONTS; i++) {
\r
1390 for (j=0; j<NUM_SIZES; j++) {
\r
1391 font[j][i] = &fontRec[j][i];
\r
1392 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1399 { // here we create the actual fonts from the selected descriptions
\r
1401 for (i=0; i<NUM_FONTS; i++) {
\r
1402 for (j=0; j<NUM_SIZES; j++) {
\r
1403 CreateFontInMF(font[j][i]);
\r
1407 /* Color name parser.
\r
1408 X version accepts X color names, but this one
\r
1409 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1411 ParseColorName(char *name)
\r
1413 int red, green, blue, count;
\r
1414 char buf[MSG_SIZ];
\r
1416 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1418 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1419 &red, &green, &blue);
\r
1422 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1423 DisplayError(buf, 0);
\r
1424 return RGB(0, 0, 0);
\r
1426 return PALETTERGB(red, green, blue);
\r
1430 ParseColor(int n, char *name)
\r
1431 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1432 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1436 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1438 char *e = argValue;
\r
1442 if (*e == 'b') eff |= CFE_BOLD;
\r
1443 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1444 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1445 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1446 else if (*e == '#' || isdigit(*e)) break;
\r
1450 *color = ParseColorName(e);
\r
1454 ParseTextAttribs(ColorClass cc, char *s)
\r
1455 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1456 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1457 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1461 ParseBoardSize(void *addr, char *name)
\r
1462 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1463 BoardSize bs = SizeTiny;
\r
1464 while (sizeInfo[bs].name != NULL) {
\r
1465 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1466 *(BoardSize *)addr = bs;
\r
1471 ExitArgError(_("Unrecognized board size value"), name, TRUE);
\r
1476 { // [HGM] import name from appData first
\r
1479 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1480 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1481 textAttribs[cc].sound.data = NULL;
\r
1482 MyLoadSound(&textAttribs[cc].sound);
\r
1484 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1485 textAttribs[cc].sound.name = strdup("");
\r
1486 textAttribs[cc].sound.data = NULL;
\r
1488 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1489 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1490 sounds[sc].data = NULL;
\r
1491 MyLoadSound(&sounds[sc]);
\r
1496 SetCommPortDefaults()
\r
1498 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1499 dcb.DCBlength = sizeof(DCB);
\r
1500 dcb.BaudRate = 9600;
\r
1501 dcb.fBinary = TRUE;
\r
1502 dcb.fParity = FALSE;
\r
1503 dcb.fOutxCtsFlow = FALSE;
\r
1504 dcb.fOutxDsrFlow = FALSE;
\r
1505 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1506 dcb.fDsrSensitivity = FALSE;
\r
1507 dcb.fTXContinueOnXoff = TRUE;
\r
1508 dcb.fOutX = FALSE;
\r
1510 dcb.fNull = FALSE;
\r
1511 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1512 dcb.fAbortOnError = FALSE;
\r
1514 dcb.Parity = SPACEPARITY;
\r
1515 dcb.StopBits = ONESTOPBIT;
\r
1518 // [HGM] args: these three cases taken out to stay in front-end
\r
1520 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1521 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1522 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1523 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1525 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1526 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1527 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1528 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1529 ad->argName, mfp->faceName, mfp->pointSize,
\r
1530 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1531 mfp->bold ? "b" : "",
\r
1532 mfp->italic ? "i" : "",
\r
1533 mfp->underline ? "u" : "",
\r
1534 mfp->strikeout ? "s" : "",
\r
1535 (int)mfp->charset);
\r
1541 { // [HGM] copy the names from the internal WB variables to appData
\r
1544 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1545 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1546 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1547 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1551 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1552 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1553 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1554 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1555 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1556 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1557 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1558 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1559 (ta->effects) ? " " : "",
\r
1560 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1564 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1565 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1566 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1567 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1568 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1572 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1573 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1574 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1578 ParseCommPortSettings(char *s)
\r
1579 { // wrapper to keep dcb from back-end
\r
1580 ParseCommSettings(s, &dcb);
\r
1585 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1586 GetActualPlacement(hwndMain, &wpMain);
\r
1587 GetActualPlacement(hwndConsole, &wpConsole);
\r
1588 GetActualPlacement(commentDialog, &wpComment);
\r
1589 GetActualPlacement(editTagsDialog, &wpTags);
\r
1590 GetActualPlacement(gameListDialog, &wpGameList);
\r
1591 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1592 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1593 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1597 PrintCommPortSettings(FILE *f, char *name)
\r
1598 { // wrapper to shield back-end from DCB
\r
1599 PrintCommSettings(f, name, &dcb);
\r
1603 MySearchPath(char *installDir, char *name, char *fullname)
\r
1605 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1606 if(name[0]== '%') {
\r
1607 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1608 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1609 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1610 *strchr(buf, '%') = 0;
\r
1611 strcat(fullname, getenv(buf));
\r
1612 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1614 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1615 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1616 return (int) strlen(fullname);
\r
1618 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1622 MyGetFullPathName(char *name, char *fullname)
\r
1625 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1630 { // [HGM] args: allows testing if main window is realized from back-end
\r
1631 return hwndMain != NULL;
\r
1635 PopUpStartupDialog()
\r
1639 LoadLanguageFile(appData.language);
\r
1640 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1641 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1642 FreeProcInstance(lpProc);
\r
1645 /*---------------------------------------------------------------------------*\
\r
1647 * GDI board drawing routines
\r
1649 \*---------------------------------------------------------------------------*/
\r
1651 /* [AS] Draw square using background texture */
\r
1652 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1657 return; /* Should never happen! */
\r
1660 SetGraphicsMode( dst, GM_ADVANCED );
\r
1667 /* X reflection */
\r
1672 x.eDx = (FLOAT) dw + dx - 1;
\r
1675 SetWorldTransform( dst, &x );
\r
1678 /* Y reflection */
\r
1684 x.eDy = (FLOAT) dh + dy - 1;
\r
1686 SetWorldTransform( dst, &x );
\r
1694 x.eDx = (FLOAT) dx;
\r
1695 x.eDy = (FLOAT) dy;
\r
1698 SetWorldTransform( dst, &x );
\r
1702 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1710 SetWorldTransform( dst, &x );
\r
1712 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1715 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1717 PM_WP = (int) WhitePawn,
\r
1718 PM_WN = (int) WhiteKnight,
\r
1719 PM_WB = (int) WhiteBishop,
\r
1720 PM_WR = (int) WhiteRook,
\r
1721 PM_WQ = (int) WhiteQueen,
\r
1722 PM_WF = (int) WhiteFerz,
\r
1723 PM_WW = (int) WhiteWazir,
\r
1724 PM_WE = (int) WhiteAlfil,
\r
1725 PM_WM = (int) WhiteMan,
\r
1726 PM_WO = (int) WhiteCannon,
\r
1727 PM_WU = (int) WhiteUnicorn,
\r
1728 PM_WH = (int) WhiteNightrider,
\r
1729 PM_WA = (int) WhiteAngel,
\r
1730 PM_WC = (int) WhiteMarshall,
\r
1731 PM_WAB = (int) WhiteCardinal,
\r
1732 PM_WD = (int) WhiteDragon,
\r
1733 PM_WL = (int) WhiteLance,
\r
1734 PM_WS = (int) WhiteCobra,
\r
1735 PM_WV = (int) WhiteFalcon,
\r
1736 PM_WSG = (int) WhiteSilver,
\r
1737 PM_WG = (int) WhiteGrasshopper,
\r
1738 PM_WK = (int) WhiteKing,
\r
1739 PM_BP = (int) BlackPawn,
\r
1740 PM_BN = (int) BlackKnight,
\r
1741 PM_BB = (int) BlackBishop,
\r
1742 PM_BR = (int) BlackRook,
\r
1743 PM_BQ = (int) BlackQueen,
\r
1744 PM_BF = (int) BlackFerz,
\r
1745 PM_BW = (int) BlackWazir,
\r
1746 PM_BE = (int) BlackAlfil,
\r
1747 PM_BM = (int) BlackMan,
\r
1748 PM_BO = (int) BlackCannon,
\r
1749 PM_BU = (int) BlackUnicorn,
\r
1750 PM_BH = (int) BlackNightrider,
\r
1751 PM_BA = (int) BlackAngel,
\r
1752 PM_BC = (int) BlackMarshall,
\r
1753 PM_BG = (int) BlackGrasshopper,
\r
1754 PM_BAB = (int) BlackCardinal,
\r
1755 PM_BD = (int) BlackDragon,
\r
1756 PM_BL = (int) BlackLance,
\r
1757 PM_BS = (int) BlackCobra,
\r
1758 PM_BV = (int) BlackFalcon,
\r
1759 PM_BSG = (int) BlackSilver,
\r
1760 PM_BK = (int) BlackKing
\r
1763 static HFONT hPieceFont = NULL;
\r
1764 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1765 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1766 static int fontBitmapSquareSize = 0;
\r
1767 static char pieceToFontChar[(int) EmptySquare] =
\r
1768 { 'p', 'n', 'b', 'r', 'q',
\r
1769 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1770 'k', 'o', 'm', 'v', 't', 'w',
\r
1771 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1774 extern BOOL SetCharTable( char *table, const char * map );
\r
1775 /* [HGM] moved to backend.c */
\r
1777 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1780 BYTE r1 = GetRValue( color );
\r
1781 BYTE g1 = GetGValue( color );
\r
1782 BYTE b1 = GetBValue( color );
\r
1788 /* Create a uniform background first */
\r
1789 hbrush = CreateSolidBrush( color );
\r
1790 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1791 FillRect( hdc, &rc, hbrush );
\r
1792 DeleteObject( hbrush );
\r
1795 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1796 int steps = squareSize / 2;
\r
1799 for( i=0; i<steps; i++ ) {
\r
1800 BYTE r = r1 - (r1-r2) * i / steps;
\r
1801 BYTE g = g1 - (g1-g2) * i / steps;
\r
1802 BYTE b = b1 - (b1-b2) * i / steps;
\r
1804 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1805 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1806 FillRect( hdc, &rc, hbrush );
\r
1807 DeleteObject(hbrush);
\r
1810 else if( mode == 2 ) {
\r
1811 /* Diagonal gradient, good more or less for every piece */
\r
1812 POINT triangle[3];
\r
1813 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1814 HBRUSH hbrush_old;
\r
1815 int steps = squareSize;
\r
1818 triangle[0].x = squareSize - steps;
\r
1819 triangle[0].y = squareSize;
\r
1820 triangle[1].x = squareSize;
\r
1821 triangle[1].y = squareSize;
\r
1822 triangle[2].x = squareSize;
\r
1823 triangle[2].y = squareSize - steps;
\r
1825 for( i=0; i<steps; i++ ) {
\r
1826 BYTE r = r1 - (r1-r2) * i / steps;
\r
1827 BYTE g = g1 - (g1-g2) * i / steps;
\r
1828 BYTE b = b1 - (b1-b2) * i / steps;
\r
1830 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1831 hbrush_old = SelectObject( hdc, hbrush );
\r
1832 Polygon( hdc, triangle, 3 );
\r
1833 SelectObject( hdc, hbrush_old );
\r
1834 DeleteObject(hbrush);
\r
1839 SelectObject( hdc, hpen );
\r
1844 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1845 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1846 piece: follow the steps as explained below.
\r
1848 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1852 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1858 int backColor = whitePieceColor;
\r
1859 int foreColor = blackPieceColor;
\r
1861 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1862 backColor = appData.fontBackColorWhite;
\r
1863 foreColor = appData.fontForeColorWhite;
\r
1865 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1866 backColor = appData.fontBackColorBlack;
\r
1867 foreColor = appData.fontForeColorBlack;
\r
1871 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1873 hbm_old = SelectObject( hdc, hbm );
\r
1877 rc.right = squareSize;
\r
1878 rc.bottom = squareSize;
\r
1880 /* Step 1: background is now black */
\r
1881 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1883 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1885 pt.x = (squareSize - sz.cx) / 2;
\r
1886 pt.y = (squareSize - sz.cy) / 2;
\r
1888 SetBkMode( hdc, TRANSPARENT );
\r
1889 SetTextColor( hdc, chroma );
\r
1890 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1891 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1893 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1894 /* Step 3: the area outside the piece is filled with white */
\r
1895 // FloodFill( hdc, 0, 0, chroma );
\r
1896 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1897 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1898 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1899 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1900 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1902 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1903 but if the start point is not inside the piece we're lost!
\r
1904 There should be a better way to do this... if we could create a region or path
\r
1905 from the fill operation we would be fine for example.
\r
1907 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1908 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1910 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1911 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1912 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1914 SelectObject( dc2, bm2 );
\r
1915 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1916 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1917 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1918 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1919 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1922 DeleteObject( bm2 );
\r
1925 SetTextColor( hdc, 0 );
\r
1927 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1928 draw the piece again in black for safety.
\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( hPieceMask[index] != NULL ) {
\r
1935 DeleteObject( hPieceMask[index] );
\r
1938 hPieceMask[index] = hbm;
\r
1941 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1943 SelectObject( hdc, hbm );
\r
1946 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1947 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1948 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1950 SelectObject( dc1, hPieceMask[index] );
\r
1951 SelectObject( dc2, bm2 );
\r
1952 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1953 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1956 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1957 the piece background and deletes (makes transparent) the rest.
\r
1958 Thanks to that mask, we are free to paint the background with the greates
\r
1959 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1960 We use this, to make gradients and give the pieces a "roundish" look.
\r
1962 SetPieceBackground( hdc, backColor, 2 );
\r
1963 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1967 DeleteObject( bm2 );
\r
1970 SetTextColor( hdc, foreColor );
\r
1971 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1973 SelectObject( hdc, hbm_old );
\r
1975 if( hPieceFace[index] != NULL ) {
\r
1976 DeleteObject( hPieceFace[index] );
\r
1979 hPieceFace[index] = hbm;
\r
1982 static int TranslatePieceToFontPiece( int piece )
\r
2012 case BlackMarshall:
\r
2016 case BlackNightrider:
\r
2022 case BlackUnicorn:
\r
2026 case BlackGrasshopper:
\r
2038 case BlackCardinal:
\r
2045 case WhiteMarshall:
\r
2049 case WhiteNightrider:
\r
2055 case WhiteUnicorn:
\r
2059 case WhiteGrasshopper:
\r
2071 case WhiteCardinal:
\r
2080 void CreatePiecesFromFont()
\r
2083 HDC hdc_window = NULL;
\r
2089 if( fontBitmapSquareSize < 0 ) {
\r
2090 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
2094 if( !appData.useFont || appData.renderPiecesWithFont == NULL ||
\r
2095 appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
2096 fontBitmapSquareSize = -1;
\r
2100 if( fontBitmapSquareSize != squareSize ) {
\r
2101 hdc_window = GetDC( hwndMain );
\r
2102 hdc = CreateCompatibleDC( hdc_window );
\r
2104 if( hPieceFont != NULL ) {
\r
2105 DeleteObject( hPieceFont );
\r
2108 for( i=0; i<=(int)BlackKing; i++ ) {
\r
2109 hPieceMask[i] = NULL;
\r
2110 hPieceFace[i] = NULL;
\r
2116 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
2117 fontHeight = appData.fontPieceSize;
\r
2120 fontHeight = (fontHeight * squareSize) / 100;
\r
2122 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2124 lf.lfEscapement = 0;
\r
2125 lf.lfOrientation = 0;
\r
2126 lf.lfWeight = FW_NORMAL;
\r
2128 lf.lfUnderline = 0;
\r
2129 lf.lfStrikeOut = 0;
\r
2130 lf.lfCharSet = DEFAULT_CHARSET;
\r
2131 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2132 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2133 lf.lfQuality = PROOF_QUALITY;
\r
2134 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2135 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2136 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2138 hPieceFont = CreateFontIndirect( &lf );
\r
2140 if( hPieceFont == NULL ) {
\r
2141 fontBitmapSquareSize = -2;
\r
2144 /* Setup font-to-piece character table */
\r
2145 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2146 /* No (or wrong) global settings, try to detect the font */
\r
2147 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2149 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2151 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2152 /* DiagramTT* family */
\r
2153 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2155 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2156 /* Fairy symbols */
\r
2157 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2159 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2160 /* Good Companion (Some characters get warped as literal :-( */
\r
2161 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2162 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2163 SetCharTable(pieceToFontChar, s);
\r
2166 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2167 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2171 /* Create bitmaps */
\r
2172 hfont_old = SelectObject( hdc, hPieceFont );
\r
2173 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2174 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2175 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2177 SelectObject( hdc, hfont_old );
\r
2179 fontBitmapSquareSize = squareSize;
\r
2183 if( hdc != NULL ) {
\r
2187 if( hdc_window != NULL ) {
\r
2188 ReleaseDC( hwndMain, hdc_window );
\r
2193 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2195 char name[128], buf[MSG_SIZ];
\r
2197 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2198 if(appData.pieceDirectory[0]) {
\r
2200 snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);
\r
2201 res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
2202 if(res) return res;
\r
2204 if (gameInfo.event &&
\r
2205 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2206 strcmp(name, "k80s") == 0) {
\r
2207 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2209 return LoadBitmap(hinst, name);
\r
2213 /* Insert a color into the program's logical palette
\r
2214 structure. This code assumes the given color is
\r
2215 the result of the RGB or PALETTERGB macro, and it
\r
2216 knows how those macros work (which is documented).
\r
2219 InsertInPalette(COLORREF color)
\r
2221 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2223 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2224 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2225 pLogPal->palNumEntries--;
\r
2229 pe->peFlags = (char) 0;
\r
2230 pe->peRed = (char) (0xFF & color);
\r
2231 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2232 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2238 InitDrawingColors()
\r
2241 if (pLogPal == NULL) {
\r
2242 /* Allocate enough memory for a logical palette with
\r
2243 * PALETTESIZE entries and set the size and version fields
\r
2244 * of the logical palette structure.
\r
2246 pLogPal = (NPLOGPALETTE)
\r
2247 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2248 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2249 pLogPal->palVersion = 0x300;
\r
2251 pLogPal->palNumEntries = 0;
\r
2253 InsertInPalette(lightSquareColor);
\r
2254 InsertInPalette(darkSquareColor);
\r
2255 InsertInPalette(whitePieceColor);
\r
2256 InsertInPalette(blackPieceColor);
\r
2257 InsertInPalette(highlightSquareColor);
\r
2258 InsertInPalette(premoveHighlightColor);
\r
2260 /* create a logical color palette according the information
\r
2261 * in the LOGPALETTE structure.
\r
2263 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2265 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2266 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2267 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2268 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2269 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2270 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2271 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2272 for(i=0; i<8;i++) markerBrush[i] = CreateSolidBrush(markerColor[i]); // [HGM] markers
\r
2274 /* [AS] Force rendering of the font-based pieces */
\r
2275 if( fontBitmapSquareSize > 0 ) {
\r
2276 fontBitmapSquareSize = 0;
\r
2282 BoardWidth(int boardSize, int n)
\r
2283 { /* [HGM] argument n added to allow different width and height */
\r
2284 int lineGap = sizeInfo[boardSize].lineGap;
\r
2286 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2287 lineGap = appData.overrideLineGap;
\r
2290 return (n + 1) * lineGap +
\r
2291 n * sizeInfo[boardSize].squareSize;
\r
2294 /* Respond to board resize by dragging edge */
\r
2296 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2298 BoardSize newSize = NUM_SIZES - 1;
\r
2299 static int recurse = 0;
\r
2300 if (IsIconic(hwndMain)) return;
\r
2301 if (recurse > 0) return;
\r
2303 while (newSize > 0) {
\r
2304 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2305 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2306 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2309 boardSize = newSize;
\r
2310 InitDrawingSizes(boardSize, flags);
\r
2315 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2318 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2320 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2321 ChessSquare piece;
\r
2322 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2324 SIZE clockSize, messageSize;
\r
2326 char buf[MSG_SIZ];
\r
2328 HMENU hmenu = GetMenu(hwndMain);
\r
2329 RECT crect, wrect, oldRect;
\r
2331 LOGBRUSH logbrush;
\r
2332 VariantClass v = gameInfo.variant;
\r
2334 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2335 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2337 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2338 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2339 if(boardSize == -1) return; // no size defined yet; abort (to allow early call of InitPosition)
\r
2340 oldBoardSize = boardSize;
\r
2342 if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)
\r
2343 { // correct board size to one where built-in pieces exist
\r
2344 if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)
\r
2345 && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range
\r
2347 || (v == VariantShogi && boardSize != SizeModerate) // Japanese-style Shogi
\r
2348 || v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan
\r
2349 || v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy || v == VariantLion ) {
\r
2350 if(boardSize < SizeMediocre) boardSize = SizePetite; else
\r
2351 if(boardSize > SizeModerate) boardSize = SizeBulky; else
\r
2352 boardSize = SizeMiddling;
\r
2355 if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite
\r
2357 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2358 oldRect.top = wpMain.y;
\r
2359 oldRect.right = wpMain.x + wpMain.width;
\r
2360 oldRect.bottom = wpMain.y + wpMain.height;
\r
2362 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2363 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2364 squareSize = sizeInfo[boardSize].squareSize;
\r
2365 lineGap = sizeInfo[boardSize].lineGap;
\r
2366 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2367 border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;
\r
2369 // [HGM] decide on tininess based on total board width rather than square size
\r
2370 tinyLayout = squareSize * (BOARD_WIDTH);
\r
2371 tinyLayout = tinyLayout < 35*8 ? 2 : tinyLayout < 43*8 ? 1 : 0;
\r
2373 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2374 lineGap = appData.overrideLineGap;
\r
2377 if (tinyLayout != oldTinyLayout) {
\r
2378 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2379 if (tinyLayout == 2) {
\r
2380 style &= ~WS_SYSMENU;
\r
2381 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2382 "&Minimize\tCtrl+F4");
\r
2384 style |= WS_SYSMENU;
\r
2385 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2387 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2389 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2390 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2391 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2393 DrawMenuBar(hwndMain);
\r
2396 boardWidth = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;
\r
2397 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;
\r
2399 /* Get text area sizes */
\r
2400 hdc = GetDC(hwndMain);
\r
2401 if (appData.clockMode) {
\r
2402 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2404 snprintf(buf, MSG_SIZ, _("White"));
\r
2406 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2407 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2408 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2409 str = _("We only care about the height here");
\r
2410 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2411 SelectObject(hdc, oldFont);
\r
2412 ReleaseDC(hwndMain, hdc);
\r
2414 /* Compute where everything goes */
\r
2415 if((first.programLogo || second.programLogo) && tinyLayout != 2) {
\r
2416 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2417 logoHeight = 2*clockSize.cy;
\r
2418 leftLogoRect.left = OUTER_MARGIN;
\r
2419 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2420 leftLogoRect.top = OUTER_MARGIN;
\r
2421 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2423 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2424 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2425 rightLogoRect.top = OUTER_MARGIN;
\r
2426 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2429 whiteRect.left = leftLogoRect.right;
\r
2430 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2431 whiteRect.top = OUTER_MARGIN;
\r
2432 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2434 blackRect.right = rightLogoRect.left;
\r
2435 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2436 blackRect.top = whiteRect.top;
\r
2437 blackRect.bottom = whiteRect.bottom;
\r
2439 whiteRect.left = OUTER_MARGIN;
\r
2440 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2441 whiteRect.top = OUTER_MARGIN;
\r
2442 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2444 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2445 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2446 blackRect.top = whiteRect.top;
\r
2447 blackRect.bottom = whiteRect.bottom;
\r
2449 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2452 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2453 if (appData.showButtonBar) {
\r
2454 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2455 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2457 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2459 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2460 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2462 boardRect.left = OUTER_MARGIN;
\r
2463 boardRect.right = boardRect.left + boardWidth;
\r
2464 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2465 boardRect.bottom = boardRect.top + boardHeight;
\r
2467 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2468 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2469 oldTinyLayout = tinyLayout;
\r
2470 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2471 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2472 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2473 winW *= 1 + twoBoards;
\r
2474 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2475 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2476 wpMain.height = winH; // without disturbing window attachments
\r
2477 GetWindowRect(hwndMain, &wrect);
\r
2478 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2479 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2481 // [HGM] placement: let attached windows follow size change.
\r
2482 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2483 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2484 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2485 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2486 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2488 /* compensate if menu bar wrapped */
\r
2489 GetClientRect(hwndMain, &crect);
\r
2490 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2491 wpMain.height += offby;
\r
2493 case WMSZ_TOPLEFT:
\r
2494 SetWindowPos(hwndMain, NULL,
\r
2495 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2496 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2499 case WMSZ_TOPRIGHT:
\r
2501 SetWindowPos(hwndMain, NULL,
\r
2502 wrect.left, wrect.bottom - wpMain.height,
\r
2503 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2506 case WMSZ_BOTTOMLEFT:
\r
2508 SetWindowPos(hwndMain, NULL,
\r
2509 wrect.right - wpMain.width, wrect.top,
\r
2510 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2513 case WMSZ_BOTTOMRIGHT:
\r
2517 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2518 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2523 for (i = 0; i < N_BUTTONS; i++) {
\r
2524 if (buttonDesc[i].hwnd != NULL) {
\r
2525 DestroyWindow(buttonDesc[i].hwnd);
\r
2526 buttonDesc[i].hwnd = NULL;
\r
2528 if (appData.showButtonBar) {
\r
2529 buttonDesc[i].hwnd =
\r
2530 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2531 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2532 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2533 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2534 (HMENU) buttonDesc[i].id,
\r
2535 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2536 if (tinyLayout == 2) {
\r
2537 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2538 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2539 MAKELPARAM(FALSE, 0));
\r
2541 if (buttonDesc[i].id == IDM_Pause)
\r
2542 hwndPause = buttonDesc[i].hwnd;
\r
2543 buttonDesc[i].wndproc = (WNDPROC)
\r
2544 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2547 if (gridPen != NULL) DeleteObject(gridPen);
\r
2548 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2549 if (premovePen != NULL) DeleteObject(premovePen);
\r
2550 if (lineGap != 0) {
\r
2551 logbrush.lbStyle = BS_SOLID;
\r
2552 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2554 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2555 lineGap, &logbrush, 0, NULL);
\r
2556 logbrush.lbColor = highlightSquareColor;
\r
2558 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2559 lineGap, &logbrush, 0, NULL);
\r
2561 logbrush.lbColor = premoveHighlightColor;
\r
2563 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2564 lineGap, &logbrush, 0, NULL);
\r
2566 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2567 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2568 gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;
\r
2569 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2570 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;
\r
2571 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2572 BOARD_WIDTH * (squareSize + lineGap) + border;
\r
2573 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2575 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2576 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;
\r
2577 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2578 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2579 lineGap / 2 + (i * (squareSize + lineGap)) + border;
\r
2580 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2581 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;
\r
2582 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2586 /* [HGM] Licensing requirement */
\r
2588 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2591 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2593 GothicPopUp( "", VariantNormal);
\r
2596 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2598 /* Load piece bitmaps for this board size */
\r
2599 for (i=0; i<=2; i++) {
\r
2600 for (piece = WhitePawn;
\r
2601 (int) piece < (int) BlackPawn;
\r
2602 piece = (ChessSquare) ((int) piece + 1)) {
\r
2603 if (pieceBitmap[i][piece] != NULL)
\r
2604 DeleteObject(pieceBitmap[i][piece]);
\r
2605 pieceBitmap[i][piece] = NULL;
\r
2609 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2611 // Orthodox Chess pieces
\r
2612 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2613 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2614 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2615 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2616 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2617 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2618 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2619 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2620 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2621 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2622 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2623 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2624 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2625 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2626 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2627 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2628 // in Shogi, Hijack the unused Queen for Lance
\r
2629 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2630 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2631 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2633 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2634 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2635 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2638 if(squareSize <= 72 && squareSize >= 33) {
\r
2639 /* A & C are available in most sizes now */
\r
2640 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2641 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2642 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2643 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2644 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2645 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2646 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2647 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2648 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2649 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2650 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2651 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2652 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2653 } else { // Smirf-like
\r
2654 if(gameInfo.variant == VariantSChess) {
\r
2655 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2656 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2657 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2659 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2660 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2661 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2664 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2665 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2666 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2667 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2668 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2669 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2670 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2671 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2672 } else { // WinBoard standard
\r
2673 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2674 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2675 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2680 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2681 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2682 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2683 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2684 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2685 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2686 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2687 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2688 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2689 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2690 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2691 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2692 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2693 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2694 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2695 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2696 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2697 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2698 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2699 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2700 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2701 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2702 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2703 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2704 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2705 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2706 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2707 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2708 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2709 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2710 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2711 pieceBitmap[0][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "s");
\r
2712 pieceBitmap[1][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "o");
\r
2713 pieceBitmap[2][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "w");
\r
2715 if(gameInfo.variant == VariantShogi && BOARD_HEIGHT != 7) { /* promoted Gold representations (but not in Tori!)*/
\r
2716 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2717 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2718 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2719 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2720 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2721 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2722 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2723 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2724 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2725 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2726 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2727 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2729 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2730 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2731 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2732 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2733 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2734 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2735 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2736 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2737 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2738 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2739 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2740 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2743 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2744 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2745 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2746 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2747 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2748 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2749 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2750 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2751 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2752 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2753 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2754 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2755 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2756 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2757 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2761 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2762 /* special Shogi support in this size */
\r
2763 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2764 for (piece = WhitePawn;
\r
2765 (int) piece < (int) BlackPawn;
\r
2766 piece = (ChessSquare) ((int) piece + 1)) {
\r
2767 if (pieceBitmap[i][piece] != NULL)
\r
2768 DeleteObject(pieceBitmap[i][piece]);
\r
2771 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2772 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2773 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2774 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2775 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2776 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2777 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2778 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2779 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2780 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2781 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2782 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2783 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2784 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2785 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2786 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2787 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2788 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2789 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2790 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2791 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2792 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2793 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2794 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2795 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2796 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2797 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2798 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2799 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2800 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2801 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2802 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2803 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2804 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2805 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2806 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2807 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2808 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2809 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2810 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2811 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2812 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2816 if(appData.pieceDirectory[0]) for(i=WhitePawn; i<BlackPawn; i++) { // try for all missing pieces with new naming convention
\r
2817 char buf[MSG_SIZ];
\r
2818 if(pieceBitmap[0][i]) continue;
\r
2819 snprintf(buf, MSG_SIZ, "piece%d_", i);
\r
2820 pieceBitmap[0][i] = DoLoadBitmap(hInst, buf, squareSize, "s");
\r
2821 pieceBitmap[1][i] = DoLoadBitmap(hInst, buf, squareSize, "o");
\r
2822 pieceBitmap[2][i] = DoLoadBitmap(hInst, buf, squareSize, "w");
\r
2827 PieceBitmap(ChessSquare p, int kind)
\r
2829 if ((int) p >= (int) BlackPawn)
\r
2830 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2832 return pieceBitmap[kind][(int) p];
\r
2835 /***************************************************************/
\r
2837 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2838 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2840 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2841 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2845 SquareToPos(int row, int column, int * x, int * y)
\r
2848 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
2849 *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;
\r
2851 *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;
\r
2852 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
2857 DrawCoordsOnDC(HDC hdc)
\r
2859 static char files[] = "0123456789012345678901221098765432109876543210";
\r
2860 static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";
\r
2861 char str[2] = { NULLCHAR, NULLCHAR };
\r
2862 int oldMode, oldAlign, x, y, start, i;
\r
2866 if (!appData.showCoords)
\r
2869 start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;
\r
2871 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2872 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2873 oldAlign = GetTextAlign(hdc);
\r
2874 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2876 y = boardRect.top + lineGap;
\r
2877 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2880 SetTextAlign(hdc, TA_RIGHT|TA_TOP);
\r
2881 x += border - lineGap - 4; y += squareSize - 6;
\r
2883 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2884 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2885 str[0] = files[start + i];
\r
2886 ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);
\r
2887 y += squareSize + lineGap;
\r
2890 start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;
\r
2893 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2894 x += -border + 4; y += border - squareSize + 6;
\r
2896 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2897 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2898 str[0] = ranks[start + i];
\r
2899 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2900 x += squareSize + lineGap;
\r
2903 SelectObject(hdc, oldBrush);
\r
2904 SetBkMode(hdc, oldMode);
\r
2905 SetTextAlign(hdc, oldAlign);
\r
2906 SelectObject(hdc, oldFont);
\r
2910 DrawGridOnDC(HDC hdc)
\r
2914 if (lineGap != 0) {
\r
2915 oldPen = SelectObject(hdc, gridPen);
\r
2916 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2917 SelectObject(hdc, oldPen);
\r
2921 #define HIGHLIGHT_PEN 0
\r
2922 #define PREMOVE_PEN 1
\r
2925 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2928 HPEN oldPen, hPen;
\r
2929 if (lineGap == 0) return;
\r
2931 x1 = boardRect.left +
\r
2932 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;
\r
2933 y1 = boardRect.top +
\r
2934 lineGap/2 + y * (squareSize + lineGap) + border;
\r
2936 x1 = boardRect.left +
\r
2937 lineGap/2 + x * (squareSize + lineGap) + border;
\r
2938 y1 = boardRect.top +
\r
2939 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;
\r
2941 hPen = pen ? premovePen : highlightPen;
\r
2942 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2943 MoveToEx(hdc, x1, y1, NULL);
\r
2944 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2945 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2946 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2947 LineTo(hdc, x1, y1);
\r
2948 SelectObject(hdc, oldPen);
\r
2952 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2955 for (i=0; i<2; i++) {
\r
2956 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2957 DrawHighlightOnDC(hdc, TRUE,
\r
2958 h->sq[i].x, h->sq[i].y,
\r
2963 /* Note: sqcolor is used only in monoMode */
\r
2964 /* Note that this code is largely duplicated in woptions.c,
\r
2965 function DrawSampleSquare, so that needs to be updated too */
\r
2967 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2969 HBITMAP oldBitmap;
\r
2973 if (appData.blindfold) return;
\r
2975 /* [AS] Use font-based pieces if needed */
\r
2976 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2977 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2978 CreatePiecesFromFont();
\r
2980 if( fontBitmapSquareSize == squareSize ) {
\r
2981 int index = TranslatePieceToFontPiece(piece);
\r
2983 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2985 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2986 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2990 squareSize, squareSize,
\r
2995 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2997 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2998 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
3002 squareSize, squareSize,
\r
3011 if (appData.monoMode) {
\r
3012 SelectObject(tmphdc, PieceBitmap(piece,
\r
3013 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
3014 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
3015 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
3017 HBRUSH xBrush = whitePieceBrush;
\r
3018 tmpSize = squareSize;
\r
3019 if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);
\r
3021 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
3022 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
3023 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
3024 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
3025 x += (squareSize - minorSize)>>1;
\r
3026 y += squareSize - minorSize - 2;
\r
3027 tmpSize = minorSize;
\r
3029 if (color || appData.allWhite ) {
\r
3030 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
3032 oldBrush = SelectObject(hdc, xBrush);
\r
3033 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
3034 if(appData.upsideDown && color==flipView)
\r
3035 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
3037 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
3038 /* Use black for outline of white pieces */
\r
3039 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
3040 if(appData.upsideDown && color==flipView)
\r
3041 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
3043 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
3044 } else if(appData.pieceDirectory[0]) {
\r
3045 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
3046 oldBrush = SelectObject(hdc, xBrush);
\r
3047 if(appData.upsideDown && color==flipView)
\r
3048 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
3050 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
3051 SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
3052 if(appData.upsideDown && color==flipView)
\r
3053 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
3055 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
3057 /* Use square color for details of black pieces */
\r
3058 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
3059 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
3060 if(appData.upsideDown && !flipView)
\r
3061 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
3063 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
3065 SelectObject(hdc, oldBrush);
\r
3066 SelectObject(tmphdc, oldBitmap);
\r
3070 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
3071 int GetBackTextureMode( int algo )
\r
3073 int result = BACK_TEXTURE_MODE_DISABLED;
\r
3077 case BACK_TEXTURE_MODE_PLAIN:
\r
3078 result = 1; /* Always use identity map */
\r
3080 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
3081 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
3089 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
3090 to handle redraws cleanly (as random numbers would always be different).
\r
3092 VOID RebuildTextureSquareInfo()
\r
3102 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
3104 if( liteBackTexture != NULL ) {
\r
3105 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3106 lite_w = bi.bmWidth;
\r
3107 lite_h = bi.bmHeight;
\r
3111 if( darkBackTexture != NULL ) {
\r
3112 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3113 dark_w = bi.bmWidth;
\r
3114 dark_h = bi.bmHeight;
\r
3118 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
3119 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
3120 if( (col + row) & 1 ) {
\r
3122 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
3123 if( lite_w >= squareSize*BOARD_WIDTH )
\r
3124 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
3126 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
3127 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
3128 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
3130 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
3131 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
3136 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
3137 if( dark_w >= squareSize*BOARD_WIDTH )
\r
3138 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
3140 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
3141 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
3142 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
3144 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
3145 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
3152 /* [AS] Arrow highlighting support */
\r
3154 static double A_WIDTH = 5; /* Width of arrow body */
\r
3156 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
3157 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
3159 static double Sqr( double x )
\r
3164 static int Round( double x )
\r
3166 return (int) (x + 0.5);
\r
3169 /* Draw an arrow between two points using current settings */
\r
3170 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
3173 double dx, dy, j, k, x, y;
\r
3175 if( d_x == s_x ) {
\r
3176 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3178 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
3181 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
3182 arrow[1].y = d_y - h;
\r
3184 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3185 arrow[2].y = d_y - h;
\r
3190 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3191 arrow[5].y = d_y - h;
\r
3193 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3194 arrow[4].y = d_y - h;
\r
3196 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3199 else if( d_y == s_y ) {
\r
3200 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3203 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3205 arrow[1].x = d_x - w;
\r
3206 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3208 arrow[2].x = d_x - w;
\r
3209 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3214 arrow[5].x = d_x - w;
\r
3215 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3217 arrow[4].x = d_x - w;
\r
3218 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3221 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3224 /* [AS] Needed a lot of paper for this! :-) */
\r
3225 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3226 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3228 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3230 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3235 arrow[0].x = Round(x - j);
\r
3236 arrow[0].y = Round(y + j*dx);
\r
3238 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3239 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3242 x = (double) d_x - k;
\r
3243 y = (double) d_y - k*dy;
\r
3246 x = (double) d_x + k;
\r
3247 y = (double) d_y + k*dy;
\r
3250 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3252 arrow[6].x = Round(x - j);
\r
3253 arrow[6].y = Round(y + j*dx);
\r
3255 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3256 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3258 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3259 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3264 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3265 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3268 Polygon( hdc, arrow, 7 );
\r
3271 /* [AS] Draw an arrow between two squares */
\r
3272 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3274 int s_x, s_y, d_x, d_y;
\r
3281 if( s_col == d_col && s_row == d_row ) {
\r
3285 /* Get source and destination points */
\r
3286 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3287 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3290 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3292 else if( d_y < s_y ) {
\r
3293 d_y += squareSize / 2 + squareSize / 4;
\r
3296 d_y += squareSize / 2;
\r
3300 d_x += squareSize / 2 - squareSize / 4;
\r
3302 else if( d_x < s_x ) {
\r
3303 d_x += squareSize / 2 + squareSize / 4;
\r
3306 d_x += squareSize / 2;
\r
3309 s_x += squareSize / 2;
\r
3310 s_y += squareSize / 2;
\r
3312 /* Adjust width */
\r
3313 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3316 stLB.lbStyle = BS_SOLID;
\r
3317 stLB.lbColor = appData.highlightArrowColor;
\r
3320 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3321 holdpen = SelectObject( hdc, hpen );
\r
3322 hbrush = CreateBrushIndirect( &stLB );
\r
3323 holdbrush = SelectObject( hdc, hbrush );
\r
3325 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3327 SelectObject( hdc, holdpen );
\r
3328 SelectObject( hdc, holdbrush );
\r
3329 DeleteObject( hpen );
\r
3330 DeleteObject( hbrush );
\r
3333 BOOL HasHighlightInfo()
\r
3335 BOOL result = FALSE;
\r
3337 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3338 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3349 BOOL IsDrawArrowEnabled()
\r
3351 BOOL result = FALSE;
\r
3353 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3360 VOID DrawArrowHighlight( HDC hdc )
\r
3362 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3363 DrawArrowBetweenSquares( hdc,
\r
3364 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3365 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3369 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3371 HRGN result = NULL;
\r
3373 if( HasHighlightInfo() ) {
\r
3374 int x1, y1, x2, y2;
\r
3375 int sx, sy, dx, dy;
\r
3377 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3378 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3380 sx = MIN( x1, x2 );
\r
3381 sy = MIN( y1, y2 );
\r
3382 dx = MAX( x1, x2 ) + squareSize;
\r
3383 dy = MAX( y1, y2 ) + squareSize;
\r
3385 result = CreateRectRgn( sx, sy, dx, dy );
\r
3392 Warning: this function modifies the behavior of several other functions.
\r
3394 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3395 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3396 repaint is scattered all over the place, which is not good for features such as
\r
3397 "arrow highlighting" that require a full repaint of the board.
\r
3399 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3400 user interaction, when speed is not so important) but especially to avoid errors
\r
3401 in the displayed graphics.
\r
3403 In such patched places, I always try refer to this function so there is a single
\r
3404 place to maintain knowledge.
\r
3406 To restore the original behavior, just return FALSE unconditionally.
\r
3408 BOOL IsFullRepaintPreferrable()
\r
3410 BOOL result = FALSE;
\r
3412 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3413 /* Arrow may appear on the board */
\r
3421 This function is called by DrawPosition to know whether a full repaint must
\r
3424 Only DrawPosition may directly call this function, which makes use of
\r
3425 some state information. Other function should call DrawPosition specifying
\r
3426 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3428 BOOL DrawPositionNeedsFullRepaint()
\r
3430 BOOL result = FALSE;
\r
3433 Probably a slightly better policy would be to trigger a full repaint
\r
3434 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3435 but animation is fast enough that it's difficult to notice.
\r
3437 if( animInfo.piece == EmptySquare ) {
\r
3438 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3446 static HBITMAP borderBitmap;
\r
3449 DrawBackgroundOnDC(HDC hdc)
\r
3455 static char oldBorder[MSG_SIZ];
\r
3456 int w = 600, h = 600, mode;
\r
3458 if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid
\r
3459 strncpy(oldBorder, appData.border, MSG_SIZ-1);
\r
3460 borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
3462 if(borderBitmap == NULL) { // loading failed, use white
\r
3463 FillRect( hdc, &boardRect, whitePieceBrush );
\r
3466 tmphdc = CreateCompatibleDC(hdc);
\r
3467 hbm = SelectObject(tmphdc, borderBitmap);
\r
3468 if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {
\r
3472 mode = SetStretchBltMode(hdc, COLORONCOLOR);
\r
3473 StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left,
\r
3474 boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3475 SetStretchBltMode(hdc, mode);
\r
3476 SelectObject(tmphdc, hbm);
\r
3481 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3483 int row, column, x, y, square_color, piece_color;
\r
3484 ChessSquare piece;
\r
3486 HDC texture_hdc = NULL;
\r
3488 /* [AS] Initialize background textures if needed */
\r
3489 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3490 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3491 if( backTextureSquareSize != squareSize
\r
3492 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3493 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3494 backTextureSquareSize = squareSize;
\r
3495 RebuildTextureSquareInfo();
\r
3498 texture_hdc = CreateCompatibleDC( hdc );
\r
3501 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3502 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3504 SquareToPos(row, column, &x, &y);
\r
3506 piece = board[row][column];
\r
3508 square_color = ((column + row) % 2) == 1;
\r
3509 if( gameInfo.variant == VariantXiangqi ) {
\r
3510 square_color = !InPalace(row, column);
\r
3511 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3512 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3514 piece_color = (int) piece < (int) BlackPawn;
\r
3517 /* [HGM] holdings file: light square or black */
\r
3518 if(column == BOARD_LEFT-2) {
\r
3519 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3522 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3526 if(column == BOARD_RGHT + 1 ) {
\r
3527 if( row < gameInfo.holdingsSize )
\r
3530 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3534 if(column == BOARD_LEFT-1 ) /* left align */
\r
3535 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3536 else if( column == BOARD_RGHT) /* right align */
\r
3537 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3538 else if( piece == DarkSquare) DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3540 if (appData.monoMode) {
\r
3541 if (piece == EmptySquare) {
\r
3542 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3543 square_color ? WHITENESS : BLACKNESS);
\r
3545 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3548 else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {
\r
3549 /* [AS] Draw the square using a texture bitmap */
\r
3550 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3551 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3552 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3555 squareSize, squareSize,
\r
3558 backTextureSquareInfo[r][c].mode,
\r
3559 backTextureSquareInfo[r][c].x,
\r
3560 backTextureSquareInfo[r][c].y );
\r
3562 SelectObject( texture_hdc, hbm );
\r
3564 if (piece != EmptySquare) {
\r
3565 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3569 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3571 oldBrush = SelectObject(hdc, brush );
\r
3572 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3573 SelectObject(hdc, oldBrush);
\r
3574 if (piece != EmptySquare)
\r
3575 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3580 if( texture_hdc != NULL ) {
\r
3581 DeleteDC( texture_hdc );
\r
3585 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3586 void fputDW(FILE *f, int x)
\r
3588 fputc(x & 255, f);
\r
3589 fputc(x>>8 & 255, f);
\r
3590 fputc(x>>16 & 255, f);
\r
3591 fputc(x>>24 & 255, f);
\r
3594 #define MAX_CLIPS 200 /* more than enough */
\r
3597 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3599 // HBITMAP bufferBitmap;
\r
3604 int w = 100, h = 50;
\r
3606 if(logo == NULL) {
\r
3607 if(!logoHeight) return;
\r
3608 FillRect( hdc, &logoRect, whitePieceBrush );
\r
3610 // GetClientRect(hwndMain, &Rect);
\r
3611 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3612 // Rect.bottom-Rect.top+1);
\r
3613 tmphdc = CreateCompatibleDC(hdc);
\r
3614 hbm = SelectObject(tmphdc, logo);
\r
3615 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3619 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3620 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3621 SelectObject(tmphdc, hbm);
\r
3629 HDC hdc = GetDC(hwndMain);
\r
3630 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3631 if(appData.autoLogo) {
\r
3633 switch(gameMode) { // pick logos based on game mode
\r
3634 case IcsObserving:
\r
3635 whiteLogo = second.programLogo; // ICS logo
\r
3636 blackLogo = second.programLogo;
\r
3639 case IcsPlayingWhite:
\r
3640 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3641 blackLogo = second.programLogo; // ICS logo
\r
3643 case IcsPlayingBlack:
\r
3644 whiteLogo = second.programLogo; // ICS logo
\r
3645 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3647 case TwoMachinesPlay:
\r
3648 if(first.twoMachinesColor[0] == 'b') {
\r
3649 whiteLogo = second.programLogo;
\r
3650 blackLogo = first.programLogo;
\r
3653 case MachinePlaysWhite:
\r
3654 blackLogo = userLogo;
\r
3656 case MachinePlaysBlack:
\r
3657 whiteLogo = userLogo;
\r
3658 blackLogo = first.programLogo;
\r
3661 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3662 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3663 ReleaseDC(hwndMain, hdc);
\r
3668 UpdateLogos(int display)
\r
3669 { // called after loading new engine(s), in tourney or from menu
\r
3670 LoadLogo(&first, 0, FALSE);
\r
3671 LoadLogo(&second, 1, appData.icsActive);
\r
3672 InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos
\r
3673 if(display) DisplayLogos();
\r
3676 static HDC hdcSeek;
\r
3678 // [HGM] seekgraph
\r
3679 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3682 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3683 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3684 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3685 SelectObject( hdcSeek, hp );
\r
3688 // front-end wrapper for drawing functions to do rectangles
\r
3689 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3694 if (hdcSeek == NULL) {
\r
3695 hdcSeek = GetDC(hwndMain);
\r
3696 if (!appData.monoMode) {
\r
3697 SelectPalette(hdcSeek, hPal, FALSE);
\r
3698 RealizePalette(hdcSeek);
\r
3701 hp = SelectObject( hdcSeek, gridPen );
\r
3702 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3703 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3704 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3705 SelectObject( hdcSeek, hp );
\r
3708 // front-end wrapper for putting text in graph
\r
3709 void DrawSeekText(char *buf, int x, int y)
\r
3712 SetBkMode( hdcSeek, TRANSPARENT );
\r
3713 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3714 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3717 void DrawSeekDot(int x, int y, int color)
\r
3719 int square = color & 0x80;
\r
3720 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3721 color == 0 ? markerBrush[1] : color == 1 ? darkSquareBrush : explodeBrush);
\r
3724 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3725 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3727 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3728 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3729 SelectObject(hdcSeek, oldBrush);
\r
3732 void DrawSeekOpen()
\r
3736 void DrawSeekClose()
\r
3741 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3743 static Board lastReq[2], lastDrawn[2];
\r
3744 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3745 static int lastDrawnFlipView = 0;
\r
3746 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3747 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3750 HBITMAP bufferBitmap;
\r
3751 HBITMAP oldBitmap;
\r
3753 HRGN clips[MAX_CLIPS];
\r
3754 ChessSquare dragged_piece = EmptySquare;
\r
3755 int nr = twoBoards*partnerUp;
\r
3757 /* I'm undecided on this - this function figures out whether a full
\r
3758 * repaint is necessary on its own, so there's no real reason to have the
\r
3759 * caller tell it that. I think this can safely be set to FALSE - but
\r
3760 * if we trust the callers not to request full repaints unnessesarily, then
\r
3761 * we could skip some clipping work. In other words, only request a full
\r
3762 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3763 * gamestart and similar) --Hawk
\r
3765 Boolean fullrepaint = repaint;
\r
3767 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3769 if( DrawPositionNeedsFullRepaint() ) {
\r
3770 fullrepaint = TRUE;
\r
3773 if (board == NULL) {
\r
3774 if (!lastReqValid[nr]) {
\r
3777 board = lastReq[nr];
\r
3779 CopyBoard(lastReq[nr], board);
\r
3780 lastReqValid[nr] = 1;
\r
3783 if (doingSizing) {
\r
3787 if (IsIconic(hwndMain)) {
\r
3791 if (hdc == NULL) {
\r
3792 hdc = GetDC(hwndMain);
\r
3793 if (!appData.monoMode) {
\r
3794 SelectPalette(hdc, hPal, FALSE);
\r
3795 RealizePalette(hdc);
\r
3799 releaseDC = FALSE;
\r
3802 /* Create some work-DCs */
\r
3803 hdcmem = CreateCompatibleDC(hdc);
\r
3804 tmphdc = CreateCompatibleDC(hdc);
\r
3806 /* If dragging is in progress, we temporarely remove the piece */
\r
3807 /* [HGM] or temporarily decrease count if stacked */
\r
3808 /* !! Moved to before board compare !! */
\r
3809 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3810 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3811 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3812 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3813 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3815 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3816 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3817 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3819 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3822 /* Figure out which squares need updating by comparing the
\r
3823 * newest board with the last drawn board and checking if
\r
3824 * flipping has changed.
\r
3826 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3827 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3828 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3829 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3830 SquareToPos(row, column, &x, &y);
\r
3831 clips[num_clips++] =
\r
3832 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3836 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3837 for (i=0; i<2; i++) {
\r
3838 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3839 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3840 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3841 lastDrawnHighlight.sq[i].y >= 0) {
\r
3842 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3843 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3844 clips[num_clips++] =
\r
3845 CreateRectRgn(x - lineGap, y - lineGap,
\r
3846 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3848 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3849 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3850 clips[num_clips++] =
\r
3851 CreateRectRgn(x - lineGap, y - lineGap,
\r
3852 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3856 for (i=0; i<2; i++) {
\r
3857 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3858 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3859 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3860 lastDrawnPremove.sq[i].y >= 0) {
\r
3861 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3862 lastDrawnPremove.sq[i].x, &x, &y);
\r
3863 clips[num_clips++] =
\r
3864 CreateRectRgn(x - lineGap, y - lineGap,
\r
3865 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3867 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3868 premoveHighlightInfo.sq[i].y >= 0) {
\r
3869 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3870 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3871 clips[num_clips++] =
\r
3872 CreateRectRgn(x - lineGap, y - lineGap,
\r
3873 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3877 } else { // nr == 1
\r
3878 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3879 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3880 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3881 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3882 for (i=0; i<2; i++) {
\r
3883 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3884 partnerHighlightInfo.sq[i].y >= 0) {
\r
3885 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3886 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3887 clips[num_clips++] =
\r
3888 CreateRectRgn(x - lineGap, y - lineGap,
\r
3889 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3891 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3892 oldPartnerHighlight.sq[i].y >= 0) {
\r
3893 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3894 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3895 clips[num_clips++] =
\r
3896 CreateRectRgn(x - lineGap, y - lineGap,
\r
3897 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3902 fullrepaint = TRUE;
\r
3905 /* Create a buffer bitmap - this is the actual bitmap
\r
3906 * being written to. When all the work is done, we can
\r
3907 * copy it to the real DC (the screen). This avoids
\r
3908 * the problems with flickering.
\r
3910 GetClientRect(hwndMain, &Rect);
\r
3911 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3912 Rect.bottom-Rect.top+1);
\r
3913 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3914 if (!appData.monoMode) {
\r
3915 SelectPalette(hdcmem, hPal, FALSE);
\r
3918 /* Create clips for dragging */
\r
3919 if (!fullrepaint) {
\r
3920 if (dragInfo.from.x >= 0) {
\r
3921 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3922 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3924 if (dragInfo.start.x >= 0) {
\r
3925 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3926 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3928 if (dragInfo.pos.x >= 0) {
\r
3929 x = dragInfo.pos.x - squareSize / 2;
\r
3930 y = dragInfo.pos.y - squareSize / 2;
\r
3931 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3933 if (dragInfo.lastpos.x >= 0) {
\r
3934 x = dragInfo.lastpos.x - squareSize / 2;
\r
3935 y = dragInfo.lastpos.y - squareSize / 2;
\r
3936 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3940 /* Are we animating a move?
\r
3942 * - remove the piece from the board (temporarely)
\r
3943 * - calculate the clipping region
\r
3945 if (!fullrepaint) {
\r
3946 if (animInfo.piece != EmptySquare) {
\r
3947 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3948 x = boardRect.left + animInfo.lastpos.x;
\r
3949 y = boardRect.top + animInfo.lastpos.y;
\r
3950 x2 = boardRect.left + animInfo.pos.x;
\r
3951 y2 = boardRect.top + animInfo.pos.y;
\r
3952 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3953 /* Slight kludge. The real problem is that after AnimateMove is
\r
3954 done, the position on the screen does not match lastDrawn.
\r
3955 This currently causes trouble only on e.p. captures in
\r
3956 atomic, where the piece moves to an empty square and then
\r
3957 explodes. The old and new positions both had an empty square
\r
3958 at the destination, but animation has drawn a piece there and
\r
3959 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3960 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3964 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3965 if (num_clips == 0)
\r
3966 fullrepaint = TRUE;
\r
3968 /* Set clipping on the memory DC */
\r
3969 if (!fullrepaint) {
\r
3970 SelectClipRgn(hdcmem, clips[0]);
\r
3971 for (x = 1; x < num_clips; x++) {
\r
3972 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3973 abort(); // this should never ever happen!
\r
3977 /* Do all the drawing to the memory DC */
\r
3978 if(explodeInfo.radius) { // [HGM] atomic
\r
3980 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3981 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3982 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3983 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3984 x += squareSize/2;
\r
3985 y += squareSize/2;
\r
3986 if(!fullrepaint) {
\r
3987 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3988 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3990 DrawGridOnDC(hdcmem);
\r
3991 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3992 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3993 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3994 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3995 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3996 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3997 SelectObject(hdcmem, oldBrush);
\r
3999 if(border) DrawBackgroundOnDC(hdcmem);
\r
4000 DrawGridOnDC(hdcmem);
\r
4001 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
4002 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
4003 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
4005 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
4006 oldPartnerHighlight = partnerHighlightInfo;
\r
4008 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
4010 if(nr == 0) // [HGM] dual: markers only on left board
\r
4011 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
4012 for (column = 0; column < BOARD_WIDTH; column++) {
\r
4013 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
4014 HBRUSH oldBrush = SelectObject(hdcmem, markerBrush[marker[row][column]-1]);
\r
4015 SquareToPos(row, column, &x, &y);
\r
4016 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
4017 x + 3*squareSize/4, y + 3*squareSize/4);
\r
4018 SelectObject(hdcmem, oldBrush);
\r
4023 if( appData.highlightMoveWithArrow ) {
\r
4025 DrawArrowHighlight(hdcmem);
\r
4028 DrawCoordsOnDC(hdcmem);
\r
4030 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
4031 /* to make sure lastDrawn contains what is actually drawn */
\r
4033 /* Put the dragged piece back into place and draw it (out of place!) */
\r
4034 if (dragged_piece != EmptySquare) {
\r
4035 /* [HGM] or restack */
\r
4036 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
4037 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
4039 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
4040 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
4042 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
4043 x = dragInfo.pos.x - squareSize / 2;
\r
4044 y = dragInfo.pos.y - squareSize / 2;
\r
4045 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
4046 ((int) dragInfo.piece < (int) BlackPawn),
\r
4047 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
4050 /* Put the animated piece back into place and draw it */
\r
4051 if (animInfo.piece != EmptySquare) {
\r
4052 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
4053 x = boardRect.left + animInfo.pos.x;
\r
4054 y = boardRect.top + animInfo.pos.y;
\r
4055 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
4056 ((int) animInfo.piece < (int) BlackPawn),
\r
4057 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
4060 /* Release the bufferBitmap by selecting in the old bitmap
\r
4061 * and delete the memory DC
\r
4063 SelectObject(hdcmem, oldBitmap);
\r
4066 /* Set clipping on the target DC */
\r
4067 if (!fullrepaint) {
\r
4068 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
4070 GetRgnBox(clips[x], &rect);
\r
4071 DeleteObject(clips[x]);
\r
4072 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
4073 rect.right + wpMain.width/2, rect.bottom);
\r
4075 SelectClipRgn(hdc, clips[0]);
\r
4076 for (x = 1; x < num_clips; x++) {
\r
4077 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
4078 abort(); // this should never ever happen!
\r
4082 /* Copy the new bitmap onto the screen in one go.
\r
4083 * This way we avoid any flickering
\r
4085 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
4086 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
4087 boardRect.right - boardRect.left,
\r
4088 boardRect.bottom - boardRect.top,
\r
4089 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
4090 if(saveDiagFlag) {
\r
4091 BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData;
\r
4092 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
4094 GetObject(bufferBitmap, sizeof(b), &b);
\r
4095 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\r
4096 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
4097 bih.biWidth = b.bmWidth;
\r
4098 bih.biHeight = b.bmHeight;
\r
4100 bih.biBitCount = b.bmBitsPixel;
\r
4101 bih.biCompression = 0;
\r
4102 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
4103 bih.biXPelsPerMeter = 0;
\r
4104 bih.biYPelsPerMeter = 0;
\r
4105 bih.biClrUsed = 0;
\r
4106 bih.biClrImportant = 0;
\r
4107 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
4108 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
4109 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
4110 // fprintf(diagFile, "%8x\n", (int) pData);
\r
4112 wb = b.bmWidthBytes;
\r
4114 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
4115 int k = ((int*) pData)[i];
\r
4116 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4117 if(j >= 16) break;
\r
4119 if(j >= nrColors) nrColors = j+1;
\r
4121 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
4123 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
4124 for(w=0; w<(wb>>2); w+=2) {
\r
4125 int k = ((int*) pData)[(wb*i>>2) + w];
\r
4126 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4127 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
4128 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
4129 pData[p++] = m | j<<4;
\r
4131 while(p&3) pData[p++] = 0;
\r
4134 wb = ((wb+31)>>5)<<2;
\r
4136 // write BITMAPFILEHEADER
\r
4137 fprintf(diagFile, "BM");
\r
4138 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
4139 fputDW(diagFile, 0);
\r
4140 fputDW(diagFile, 0x36 + (fac?64:0));
\r
4141 // write BITMAPINFOHEADER
\r
4142 fputDW(diagFile, 40);
\r
4143 fputDW(diagFile, b.bmWidth);
\r
4144 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
4145 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
4146 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
4147 fputDW(diagFile, 0);
\r
4148 fputDW(diagFile, 0);
\r
4149 fputDW(diagFile, 0);
\r
4150 fputDW(diagFile, 0);
\r
4151 fputDW(diagFile, 0);
\r
4152 fputDW(diagFile, 0);
\r
4153 // write color table
\r
4155 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
4156 // write bitmap data
\r
4157 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
4158 fputc(pData[i], diagFile);
\r
4163 SelectObject(tmphdc, oldBitmap);
\r
4165 /* Massive cleanup */
\r
4166 for (x = 0; x < num_clips; x++)
\r
4167 DeleteObject(clips[x]);
\r
4170 DeleteObject(bufferBitmap);
\r
4173 ReleaseDC(hwndMain, hdc);
\r
4175 if (lastDrawnFlipView != flipView && nr == 0) {
\r
4177 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
4179 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
4182 /* CopyBoard(lastDrawn, board);*/
\r
4183 lastDrawnHighlight = highlightInfo;
\r
4184 lastDrawnPremove = premoveHighlightInfo;
\r
4185 lastDrawnFlipView = flipView;
\r
4186 lastDrawnValid[nr] = 1;
\r
4189 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
4194 saveDiagFlag = 1; diagFile = f;
\r
4195 HDCDrawPosition(NULL, TRUE, NULL);
\r
4203 /*---------------------------------------------------------------------------*\
\r
4204 | CLIENT PAINT PROCEDURE
\r
4205 | This is the main event-handler for the WM_PAINT message.
\r
4207 \*---------------------------------------------------------------------------*/
\r
4209 PaintProc(HWND hwnd)
\r
4215 if((hdc = BeginPaint(hwnd, &ps))) {
\r
4216 if (IsIconic(hwnd)) {
\r
4217 DrawIcon(hdc, 2, 2, iconCurrent);
\r
4219 if (!appData.monoMode) {
\r
4220 SelectPalette(hdc, hPal, FALSE);
\r
4221 RealizePalette(hdc);
\r
4223 HDCDrawPosition(hdc, 1, NULL);
\r
4224 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
4225 flipView = !flipView; partnerUp = !partnerUp;
\r
4226 HDCDrawPosition(hdc, 1, NULL);
\r
4227 flipView = !flipView; partnerUp = !partnerUp;
\r
4230 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
4231 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
4232 ETO_CLIPPED|ETO_OPAQUE,
\r
4233 &messageRect, messageText, strlen(messageText), NULL);
\r
4234 SelectObject(hdc, oldFont);
\r
4235 DisplayBothClocks();
\r
4238 EndPaint(hwnd,&ps);
\r
4246 * If the user selects on a border boundary, return -1; if off the board,
\r
4247 * return -2. Otherwise map the event coordinate to the square.
\r
4248 * The offset boardRect.left or boardRect.top must already have been
\r
4249 * subtracted from x.
\r
4251 int EventToSquare(x, limit)
\r
4256 if (x < lineGap + border)
\r
4258 x -= lineGap + border;
\r
4259 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4261 x /= (squareSize + lineGap);
\r
4273 DropEnable dropEnables[] = {
\r
4274 { 'P', DP_Pawn, N_("Pawn") },
\r
4275 { 'N', DP_Knight, N_("Knight") },
\r
4276 { 'B', DP_Bishop, N_("Bishop") },
\r
4277 { 'R', DP_Rook, N_("Rook") },
\r
4278 { 'Q', DP_Queen, N_("Queen") },
\r
4282 SetupDropMenu(HMENU hmenu)
\r
4284 int i, count, enable;
\r
4286 extern char white_holding[], black_holding[];
\r
4287 char item[MSG_SIZ];
\r
4289 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4290 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4291 dropEnables[i].piece);
\r
4293 while (p && *p++ == dropEnables[i].piece) count++;
\r
4294 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4295 enable = count > 0 || !appData.testLegality
\r
4296 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4297 && !appData.icsActive);
\r
4298 ModifyMenu(hmenu, dropEnables[i].command,
\r
4299 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4300 dropEnables[i].command, item);
\r
4304 void DragPieceBegin(int x, int y, Boolean instantly)
\r
4306 dragInfo.lastpos.x = boardRect.left + x;
\r
4307 dragInfo.lastpos.y = boardRect.top + y;
\r
4308 if(instantly) dragInfo.pos = dragInfo.lastpos;
\r
4309 dragInfo.from.x = fromX;
\r
4310 dragInfo.from.y = fromY;
\r
4311 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4312 dragInfo.start = dragInfo.from;
\r
4313 SetCapture(hwndMain);
\r
4316 void DragPieceEnd(int x, int y)
\r
4319 dragInfo.start.x = dragInfo.start.y = -1;
\r
4320 dragInfo.from = dragInfo.start;
\r
4321 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4324 void ChangeDragPiece(ChessSquare piece)
\r
4326 dragInfo.piece = piece;
\r
4329 /* Event handler for mouse messages */
\r
4331 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4335 static int recursive = 0;
\r
4337 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4340 if (message == WM_MBUTTONUP) {
\r
4341 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4342 to the middle button: we simulate pressing the left button too!
\r
4344 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4345 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4351 pt.x = LOWORD(lParam);
\r
4352 pt.y = HIWORD(lParam);
\r
4353 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4354 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4355 if (!flipView && y >= 0) {
\r
4356 y = BOARD_HEIGHT - 1 - y;
\r
4358 if (flipView && x >= 0) {
\r
4359 x = BOARD_WIDTH - 1 - x;
\r
4362 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4363 controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status
\r
4365 switch (message) {
\r
4366 case WM_LBUTTONDOWN:
\r
4367 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4368 ClockClick(flipClock); break;
\r
4369 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4370 ClockClick(!flipClock); break;
\r
4372 if(dragging) { // [HGM] lion: don't destroy dragging info if we are already dragging
\r
4373 dragInfo.start.x = dragInfo.start.y = -1;
\r
4374 dragInfo.from = dragInfo.start;
\r
4376 if(fromX == -1 && frozen) { // not sure where this is for
\r
4377 fromX = fromY = -1;
\r
4378 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4381 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4382 DrawPosition(TRUE, NULL);
\r
4385 case WM_LBUTTONUP:
\r
4386 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4387 DrawPosition(TRUE, NULL);
\r
4390 case WM_MOUSEMOVE:
\r
4391 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4392 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4393 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4394 if ((appData.animateDragging || appData.highlightDragging)
\r
4395 && (wParam & MK_LBUTTON || dragging == 2)
\r
4396 && dragInfo.from.x >= 0)
\r
4398 BOOL full_repaint = FALSE;
\r
4400 if (appData.animateDragging) {
\r
4401 dragInfo.pos = pt;
\r
4403 if (appData.highlightDragging) {
\r
4404 HoverEvent(highlightInfo.sq[1].x, highlightInfo.sq[1].y, x, y);
\r
4405 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4406 full_repaint = TRUE;
\r
4410 DrawPosition( full_repaint, NULL);
\r
4412 dragInfo.lastpos = dragInfo.pos;
\r
4416 case WM_MOUSEWHEEL: // [DM]
\r
4417 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4418 /* Mouse Wheel is being rolled forward
\r
4419 * Play moves forward
\r
4421 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4422 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4423 /* Mouse Wheel is being rolled backward
\r
4424 * Play moves backward
\r
4426 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4427 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4431 case WM_MBUTTONUP:
\r
4432 case WM_RBUTTONUP:
\r
4434 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4437 case WM_MBUTTONDOWN:
\r
4438 case WM_RBUTTONDOWN:
\r
4441 fromX = fromY = -1;
\r
4442 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4443 dragInfo.start.x = dragInfo.start.y = -1;
\r
4444 dragInfo.from = dragInfo.start;
\r
4445 dragInfo.lastpos = dragInfo.pos;
\r
4446 if (appData.highlightDragging) {
\r
4447 ClearHighlights();
\r
4450 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4451 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4452 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4453 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4454 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4458 DrawPosition(TRUE, NULL);
\r
4460 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4463 if (message == WM_MBUTTONDOWN) {
\r
4464 buttonCount = 3; /* even if system didn't think so */
\r
4465 if (wParam & MK_SHIFT)
\r
4466 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4468 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4469 } else { /* message == WM_RBUTTONDOWN */
\r
4470 /* Just have one menu, on the right button. Windows users don't
\r
4471 think to try the middle one, and sometimes other software steals
\r
4472 it, or it doesn't really exist. */
\r
4473 if(gameInfo.variant != VariantShogi)
\r
4474 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4476 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4480 SetCapture(hwndMain);
\r
4483 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4484 SetupDropMenu(hmenu);
\r
4485 MenuPopup(hwnd, pt, hmenu, -1);
\r
4495 /* Preprocess messages for buttons in main window */
\r
4497 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4499 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4502 for (i=0; i<N_BUTTONS; i++) {
\r
4503 if (buttonDesc[i].id == id) break;
\r
4505 if (i == N_BUTTONS) return 0;
\r
4506 switch (message) {
\r
4511 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4512 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4519 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4522 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4523 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4524 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4525 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4527 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4529 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4530 TypeInEvent((char)wParam);
\r
4536 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4539 static int promoStyle;
\r
4541 /* Process messages for Promotion dialog box */
\r
4543 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4548 switch (message) {
\r
4550 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4551 /* Center the dialog over the application window */
\r
4552 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4553 Translate(hDlg, DLG_PromotionKing);
\r
4554 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4555 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4556 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4557 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4558 SW_SHOW : SW_HIDE);
\r
4559 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4560 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4561 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4562 PieceToChar(WhiteAngel) != '~') ||
\r
4563 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4564 PieceToChar(BlackAngel) != '~') ) ?
\r
4565 SW_SHOW : SW_HIDE);
\r
4566 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4567 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4568 PieceToChar(WhiteMarshall) != '~') ||
\r
4569 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4570 PieceToChar(BlackMarshall) != '~') ) ?
\r
4571 SW_SHOW : SW_HIDE);
\r
4572 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4573 ShowWindow(GetDlgItem(hDlg, PB_Rook), !promoStyle ? SW_SHOW : SW_HIDE);
\r
4574 ShowWindow(GetDlgItem(hDlg, PB_Bishop), !promoStyle ? SW_SHOW : SW_HIDE);
\r
4576 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4577 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4578 SetWindowText(hDlg, "Promote?");
\r
4580 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4581 gameInfo.variant == VariantSuper ?
\r
4582 SW_SHOW : SW_HIDE);
\r
4585 case WM_COMMAND: /* message: received a command */
\r
4586 switch (LOWORD(wParam)) {
\r
4588 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4589 ClearHighlights();
\r
4590 DrawPosition(FALSE, NULL);
\r
4593 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4596 promoChar = promoStyle ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4599 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4600 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4603 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4604 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4606 case PB_Chancellor:
\r
4607 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4609 case PB_Archbishop:
\r
4610 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4613 promoChar = gameInfo.variant == VariantShogi ? '=' : promoStyle ? NULLCHAR :
\r
4614 ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight));
\r
4619 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4620 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4621 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4622 fromX = fromY = -1;
\r
4623 if (!appData.highlightLastMove) {
\r
4624 ClearHighlights();
\r
4625 DrawPosition(FALSE, NULL);
\r
4632 /* Pop up promotion dialog */
\r
4634 PromotionPopup(HWND hwnd)
\r
4638 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4639 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4640 hwnd, (DLGPROC)lpProc);
\r
4641 FreeProcInstance(lpProc);
\r
4645 PromotionPopUp(char choice)
\r
4647 promoStyle = (choice == '+' || IS_SHOGI(gameInfo.variant));
\r
4648 DrawPosition(TRUE, NULL);
\r
4649 PromotionPopup(hwndMain);
\r
4653 LoadGameDialog(HWND hwnd, char* title)
\r
4657 char fileTitle[MSG_SIZ];
\r
4658 f = OpenFileDialog(hwnd, "rb", "",
\r
4659 appData.oldSaveStyle ? "gam" : "pgn",
\r
4661 title, &number, fileTitle, NULL);
\r
4663 cmailMsgLoaded = FALSE;
\r
4664 if (number == 0) {
\r
4665 int error = GameListBuild(f);
\r
4667 DisplayError(_("Cannot build game list"), error);
\r
4668 } else if (!ListEmpty(&gameList) &&
\r
4669 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4670 GameListPopUp(f, fileTitle);
\r
4673 GameListDestroy();
\r
4676 LoadGame(f, number, fileTitle, FALSE);
\r
4680 int get_term_width()
\r
4685 HFONT hfont, hold_font;
\r
4690 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4694 // get the text metrics
\r
4695 hdc = GetDC(hText);
\r
4696 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4697 if (consoleCF.dwEffects & CFE_BOLD)
\r
4698 lf.lfWeight = FW_BOLD;
\r
4699 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4700 lf.lfItalic = TRUE;
\r
4701 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4702 lf.lfStrikeOut = TRUE;
\r
4703 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4704 lf.lfUnderline = TRUE;
\r
4705 hfont = CreateFontIndirect(&lf);
\r
4706 hold_font = SelectObject(hdc, hfont);
\r
4707 GetTextMetrics(hdc, &tm);
\r
4708 SelectObject(hdc, hold_font);
\r
4709 DeleteObject(hfont);
\r
4710 ReleaseDC(hText, hdc);
\r
4712 // get the rectangle
\r
4713 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4715 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4718 void UpdateICSWidth(HWND hText)
\r
4720 LONG old_width, new_width;
\r
4722 new_width = get_term_width(hText, FALSE);
\r
4723 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4724 if (new_width != old_width)
\r
4726 ics_update_width(new_width);
\r
4727 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4732 ChangedConsoleFont()
\r
4735 CHARRANGE tmpsel, sel;
\r
4736 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4737 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4738 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4741 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4742 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4743 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4744 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4745 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4746 * size. This was undocumented in the version of MSVC++ that I had
\r
4747 * when I wrote the code, but is apparently documented now.
\r
4749 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4750 cfmt.bCharSet = f->lf.lfCharSet;
\r
4751 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4752 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4753 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4754 /* Why are the following seemingly needed too? */
\r
4755 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4756 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4757 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4759 tmpsel.cpMax = -1; /*999999?*/
\r
4760 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4761 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4762 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4763 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4765 paraf.cbSize = sizeof(paraf);
\r
4766 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4767 paraf.dxStartIndent = 0;
\r
4768 paraf.dxOffset = WRAP_INDENT;
\r
4769 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4770 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4771 UpdateICSWidth(hText);
\r
4774 /*---------------------------------------------------------------------------*\
\r
4776 * Window Proc for main window
\r
4778 \*---------------------------------------------------------------------------*/
\r
4780 /* Process messages for main window, etc. */
\r
4782 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4789 char fileTitle[MSG_SIZ];
\r
4790 static SnapData sd;
\r
4791 static int peek=0;
\r
4793 switch (message) {
\r
4795 case WM_PAINT: /* message: repaint portion of window */
\r
4799 case WM_ERASEBKGND:
\r
4800 if (IsIconic(hwnd)) {
\r
4801 /* Cheat; change the message */
\r
4802 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4804 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4808 case WM_LBUTTONDOWN:
\r
4809 case WM_MBUTTONDOWN:
\r
4810 case WM_RBUTTONDOWN:
\r
4811 case WM_LBUTTONUP:
\r
4812 case WM_MBUTTONUP:
\r
4813 case WM_RBUTTONUP:
\r
4814 case WM_MOUSEMOVE:
\r
4815 case WM_MOUSEWHEEL:
\r
4816 MouseEvent(hwnd, message, wParam, lParam);
\r
4820 if((char)wParam == '\b') {
\r
4821 ForwardEvent(); peek = 0;
\r
4824 JAWS_KBUP_NAVIGATION
\r
4829 if((char)wParam == '\b') {
\r
4830 if(!peek) BackwardEvent(), peek = 1;
\r
4833 JAWS_KBDOWN_NAVIGATION
\r
4839 JAWS_ALT_INTERCEPT
\r
4841 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4842 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4843 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4844 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4846 SendMessage(h, message, wParam, lParam);
\r
4847 } else if(lParam != KF_REPEAT) {
\r
4848 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4849 TypeInEvent((char)wParam);
\r
4850 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4851 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4856 case WM_PALETTECHANGED:
\r
4857 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4859 HDC hdc = GetDC(hwndMain);
\r
4860 SelectPalette(hdc, hPal, TRUE);
\r
4861 nnew = RealizePalette(hdc);
\r
4863 paletteChanged = TRUE;
\r
4865 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4867 ReleaseDC(hwnd, hdc);
\r
4871 case WM_QUERYNEWPALETTE:
\r
4872 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4874 HDC hdc = GetDC(hwndMain);
\r
4875 paletteChanged = FALSE;
\r
4876 SelectPalette(hdc, hPal, FALSE);
\r
4877 nnew = RealizePalette(hdc);
\r
4879 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4881 ReleaseDC(hwnd, hdc);
\r
4886 case WM_COMMAND: /* message: command from application menu */
\r
4887 wmId = LOWORD(wParam);
\r
4892 SAY("new game enter a move to play against the computer with white");
\r
4895 case IDM_NewGameFRC:
\r
4896 if( NewGameFRC() == 0 ) {
\r
4901 case IDM_NewVariant:
\r
4902 NewVariantPopup(hwnd);
\r
4905 case IDM_LoadGame:
\r
4906 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4909 case IDM_LoadNextGame:
\r
4913 case IDM_LoadPrevGame:
\r
4917 case IDM_ReloadGame:
\r
4921 case IDM_LoadPosition:
\r
4922 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4923 Reset(FALSE, TRUE);
\r
4926 f = OpenFileDialog(hwnd, "rb", "",
\r
4927 appData.oldSaveStyle ? "pos" : "fen",
\r
4929 _("Load Position from File"), &number, fileTitle, NULL);
\r
4931 LoadPosition(f, number, fileTitle);
\r
4935 case IDM_LoadNextPosition:
\r
4936 ReloadPosition(1);
\r
4939 case IDM_LoadPrevPosition:
\r
4940 ReloadPosition(-1);
\r
4943 case IDM_ReloadPosition:
\r
4944 ReloadPosition(0);
\r
4947 case IDM_SaveGame:
\r
4948 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4949 f = OpenFileDialog(hwnd, "a", defName,
\r
4950 appData.oldSaveStyle ? "gam" : "pgn",
\r
4952 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4954 SaveGame(f, 0, "");
\r
4958 case IDM_SavePosition:
\r
4959 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4960 f = OpenFileDialog(hwnd, "a", defName,
\r
4961 appData.oldSaveStyle ? "pos" : "fen",
\r
4963 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4965 SavePosition(f, 0, "");
\r
4969 case IDM_SaveDiagram:
\r
4970 defName = "diagram";
\r
4971 f = OpenFileDialog(hwnd, "wb", defName,
\r
4974 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
4980 case IDM_SaveSelected:
\r
4981 f = OpenFileDialog(hwnd, "a", "",
\r
4984 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4986 SaveSelected(f, 0, "");
\r
4990 case IDM_CreateBook:
\r
4991 CreateBookEvent();
\r
4994 case IDM_CopyGame:
\r
4995 CopyGameToClipboard();
\r
4998 case IDM_PasteGame:
\r
4999 PasteGameFromClipboard();
\r
5002 case IDM_CopyGameListToClipboard:
\r
5003 CopyGameListToClipboard();
\r
5006 /* [AS] Autodetect FEN or PGN data */
\r
5007 case IDM_PasteAny:
\r
5008 PasteGameOrFENFromClipboard();
\r
5011 /* [AS] Move history */
\r
5012 case IDM_ShowMoveHistory:
\r
5013 if( MoveHistoryIsUp() ) {
\r
5014 MoveHistoryPopDown();
\r
5017 MoveHistoryPopUp();
\r
5021 /* [AS] Eval graph */
\r
5022 case IDM_ShowEvalGraph:
\r
5023 if( EvalGraphIsUp() ) {
\r
5024 EvalGraphPopDown();
\r
5028 SetFocus(hwndMain);
\r
5032 /* [AS] Engine output */
\r
5033 case IDM_ShowEngineOutput:
\r
5034 if( EngineOutputIsUp() ) {
\r
5035 EngineOutputPopDown();
\r
5038 EngineOutputPopUp();
\r
5042 /* [AS] User adjudication */
\r
5043 case IDM_UserAdjudication_White:
\r
5044 UserAdjudicationEvent( +1 );
\r
5047 case IDM_UserAdjudication_Black:
\r
5048 UserAdjudicationEvent( -1 );
\r
5051 case IDM_UserAdjudication_Draw:
\r
5052 UserAdjudicationEvent( 0 );
\r
5055 /* [AS] Game list options dialog */
\r
5056 case IDM_GameListOptions:
\r
5057 GameListOptions();
\r
5064 case IDM_CopyPosition:
\r
5065 CopyFENToClipboard();
\r
5068 case IDM_PastePosition:
\r
5069 PasteFENFromClipboard();
\r
5072 case IDM_MailMove:
\r
5076 case IDM_ReloadCMailMsg:
\r
5077 Reset(TRUE, TRUE);
\r
5078 ReloadCmailMsgEvent(FALSE);
\r
5081 case IDM_Minimize:
\r
5082 ShowWindow(hwnd, SW_MINIMIZE);
\r
5089 case IDM_MachineWhite:
\r
5090 MachineWhiteEvent();
\r
5092 * refresh the tags dialog only if it's visible
\r
5094 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
5096 tags = PGNTags(&gameInfo);
\r
5097 TagsPopUp(tags, CmailMsg());
\r
5100 SAY("computer starts playing white");
\r
5103 case IDM_MachineBlack:
\r
5104 MachineBlackEvent();
\r
5106 * refresh the tags dialog only if it's visible
\r
5108 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
5110 tags = PGNTags(&gameInfo);
\r
5111 TagsPopUp(tags, CmailMsg());
\r
5114 SAY("computer starts playing black");
\r
5117 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
5118 if(matchMode) EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_GRAYED);
\r
5119 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
5122 case IDM_TwoMachines:
\r
5123 TwoMachinesEvent();
\r
5126 * refresh the tags dialog only if it's visible
\r
5128 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
5130 tags = PGNTags(&gameInfo);
\r
5131 TagsPopUp(tags, CmailMsg());
\r
5134 SAY("computer starts playing both sides");
\r
5137 case IDM_AnalysisMode:
\r
5138 if(AnalyzeModeEvent()) {
\r
5139 SAY("analyzing current position");
\r
5143 case IDM_AnalyzeFile:
\r
5144 AnalyzeFileEvent();
\r
5147 case IDM_IcsClient:
\r
5151 case IDM_EditGame:
\r
5152 case IDM_EditGame2:
\r
5157 case IDM_EditPosition:
\r
5158 case IDM_EditPosition2:
\r
5159 EditPositionEvent();
\r
5160 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
5163 case IDM_Training:
\r
5167 case IDM_ShowGameList:
\r
5168 ShowGameListProc();
\r
5171 case IDM_EditProgs1:
\r
5172 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
5175 case IDM_LoadProg1:
\r
5176 LoadEnginePopUp(hwndMain, 0);
\r
5179 case IDM_LoadProg2:
\r
5180 LoadEnginePopUp(hwndMain, 1);
\r
5183 case IDM_EditServers:
\r
5184 EditTagsPopUp(icsNames, &icsNames);
\r
5187 case IDM_EditTags:
\r
5192 case IDM_EditBook:
\r
5196 case IDM_EditComment:
\r
5198 if (commentUp && editComment) {
\r
5201 EditCommentEvent();
\r
5222 case IDM_CallFlag:
\r
5242 case IDM_StopObserving:
\r
5243 StopObservingEvent();
\r
5246 case IDM_StopExamining:
\r
5247 StopExaminingEvent();
\r
5251 UploadGameEvent();
\r
5254 case IDM_TypeInMove:
\r
5255 TypeInEvent('\000');
\r
5258 case IDM_TypeInName:
\r
5259 PopUpNameDialog('\000');
\r
5262 case IDM_Backward:
\r
5264 SetFocus(hwndMain);
\r
5271 SetFocus(hwndMain);
\r
5276 SetFocus(hwndMain);
\r
5281 SetFocus(hwndMain);
\r
5284 case OPT_GameListNext: // [HGM] forward these two accelerators to Game List
\r
5285 case OPT_GameListPrev:
\r
5286 if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);
\r
5290 RevertEvent(FALSE);
\r
5293 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5294 RevertEvent(TRUE);
\r
5297 case IDM_TruncateGame:
\r
5298 TruncateGameEvent();
\r
5305 case IDM_RetractMove:
\r
5306 RetractMoveEvent();
\r
5309 case IDM_FlipView:
\r
5310 flipView = !flipView;
\r
5311 DrawPosition(FALSE, NULL);
\r
5314 case IDM_FlipClock:
\r
5315 flipClock = !flipClock;
\r
5316 DisplayBothClocks();
\r
5320 case IDM_MuteSounds:
\r
5321 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5322 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5323 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5326 case IDM_GeneralOptions:
\r
5327 GeneralOptionsPopup(hwnd);
\r
5328 DrawPosition(TRUE, NULL);
\r
5331 case IDM_BoardOptions:
\r
5332 BoardOptionsPopup(hwnd);
\r
5335 case IDM_ThemeOptions:
\r
5336 ThemeOptionsPopup(hwnd);
\r
5339 case IDM_EnginePlayOptions:
\r
5340 EnginePlayOptionsPopup(hwnd);
\r
5343 case IDM_Engine1Options:
\r
5344 EngineOptionsPopup(hwnd, &first);
\r
5347 case IDM_Engine2Options:
\r
5349 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5350 EngineOptionsPopup(hwnd, &second);
\r
5353 case IDM_OptionsUCI:
\r
5354 UciOptionsPopup(hwnd);
\r
5358 TourneyPopup(hwnd);
\r
5361 case IDM_IcsOptions:
\r
5362 IcsOptionsPopup(hwnd);
\r
5366 FontsOptionsPopup(hwnd);
\r
5370 SoundOptionsPopup(hwnd);
\r
5373 case IDM_CommPort:
\r
5374 CommPortOptionsPopup(hwnd);
\r
5377 case IDM_LoadOptions:
\r
5378 LoadOptionsPopup(hwnd);
\r
5381 case IDM_SaveOptions:
\r
5382 SaveOptionsPopup(hwnd);
\r
5385 case IDM_TimeControl:
\r
5386 TimeControlOptionsPopup(hwnd);
\r
5389 case IDM_SaveSettings:
\r
5390 SaveSettings(settingsFileName);
\r
5393 case IDM_SaveSettingsOnExit:
\r
5394 saveSettingsOnExit = !saveSettingsOnExit;
\r
5395 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5396 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5397 MF_CHECKED : MF_UNCHECKED));
\r
5408 case IDM_AboutGame:
\r
5413 appData.debugMode = !appData.debugMode;
\r
5414 if (appData.debugMode) {
\r
5415 char dir[MSG_SIZ];
\r
5416 GetCurrentDirectory(MSG_SIZ, dir);
\r
5417 SetCurrentDirectory(installDir);
\r
5418 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5419 SetCurrentDirectory(dir);
\r
5420 setbuf(debugFP, NULL);
\r
5427 case IDM_HELPCONTENTS:
\r
5428 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5429 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5430 MessageBox (GetFocus(),
\r
5431 _("Unable to activate help"),
\r
5432 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5436 case IDM_HELPSEARCH:
\r
5437 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5438 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5439 MessageBox (GetFocus(),
\r
5440 _("Unable to activate help"),
\r
5441 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5445 case IDM_HELPHELP:
\r
5446 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5447 MessageBox (GetFocus(),
\r
5448 _("Unable to activate help"),
\r
5449 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5454 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5456 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5457 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5458 FreeProcInstance(lpProc);
\r
5461 case IDM_DirectCommand1:
\r
5462 AskQuestionEvent(_("Direct Command"),
\r
5463 _("Send to chess program:"), "", "1");
\r
5465 case IDM_DirectCommand2:
\r
5466 AskQuestionEvent(_("Direct Command"),
\r
5467 _("Send to second chess program:"), "", "2");
\r
5470 case EP_WhitePawn:
\r
5471 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5472 fromX = fromY = -1;
\r
5475 case EP_WhiteKnight:
\r
5476 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5477 fromX = fromY = -1;
\r
5480 case EP_WhiteBishop:
\r
5481 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5482 fromX = fromY = -1;
\r
5485 case EP_WhiteRook:
\r
5486 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5487 fromX = fromY = -1;
\r
5490 case EP_WhiteQueen:
\r
5491 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5492 fromX = fromY = -1;
\r
5495 case EP_WhiteFerz:
\r
5496 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5497 fromX = fromY = -1;
\r
5500 case EP_WhiteWazir:
\r
5501 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5502 fromX = fromY = -1;
\r
5505 case EP_WhiteAlfil:
\r
5506 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5507 fromX = fromY = -1;
\r
5510 case EP_WhiteCannon:
\r
5511 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5512 fromX = fromY = -1;
\r
5515 case EP_WhiteCardinal:
\r
5516 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5517 fromX = fromY = -1;
\r
5520 case EP_WhiteMarshall:
\r
5521 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5522 fromX = fromY = -1;
\r
5525 case EP_WhiteKing:
\r
5526 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5527 fromX = fromY = -1;
\r
5530 case EP_BlackPawn:
\r
5531 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5532 fromX = fromY = -1;
\r
5535 case EP_BlackKnight:
\r
5536 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5537 fromX = fromY = -1;
\r
5540 case EP_BlackBishop:
\r
5541 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5542 fromX = fromY = -1;
\r
5545 case EP_BlackRook:
\r
5546 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5547 fromX = fromY = -1;
\r
5550 case EP_BlackQueen:
\r
5551 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5552 fromX = fromY = -1;
\r
5555 case EP_BlackFerz:
\r
5556 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5557 fromX = fromY = -1;
\r
5560 case EP_BlackWazir:
\r
5561 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5562 fromX = fromY = -1;
\r
5565 case EP_BlackAlfil:
\r
5566 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5567 fromX = fromY = -1;
\r
5570 case EP_BlackCannon:
\r
5571 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5572 fromX = fromY = -1;
\r
5575 case EP_BlackCardinal:
\r
5576 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5577 fromX = fromY = -1;
\r
5580 case EP_BlackMarshall:
\r
5581 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5582 fromX = fromY = -1;
\r
5585 case EP_BlackKing:
\r
5586 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5587 fromX = fromY = -1;
\r
5590 case EP_EmptySquare:
\r
5591 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5592 fromX = fromY = -1;
\r
5595 case EP_ClearBoard:
\r
5596 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5597 fromX = fromY = -1;
\r
5601 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5602 fromX = fromY = -1;
\r
5606 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5607 fromX = fromY = -1;
\r
5611 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5612 fromX = fromY = -1;
\r
5616 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5617 fromX = fromY = -1;
\r
5621 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5622 fromX = fromY = -1;
\r
5626 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5627 fromX = fromY = -1;
\r
5631 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5632 fromX = fromY = -1;
\r
5636 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5637 fromX = fromY = -1;
\r
5641 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5642 fromX = fromY = -1;
\r
5646 barbaric = 0; appData.language = "";
\r
5647 TranslateMenus(0);
\r
5648 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5649 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5650 lastChecked = wmId;
\r
5654 if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)
\r
5655 RecentEngineEvent(wmId - IDM_RecentEngines);
\r
5657 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5658 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5659 TranslateMenus(0);
\r
5660 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5661 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5662 lastChecked = wmId;
\r
5665 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5671 case CLOCK_TIMER_ID:
\r
5672 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5673 clockTimerEvent = 0;
\r
5674 DecrementClocks(); /* call into back end */
\r
5676 case LOAD_GAME_TIMER_ID:
\r
5677 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5678 loadGameTimerEvent = 0;
\r
5679 AutoPlayGameLoop(); /* call into back end */
\r
5681 case ANALYSIS_TIMER_ID:
\r
5682 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5683 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5684 AnalysisPeriodicEvent(0);
\r
5686 KillTimer(hwnd, analysisTimerEvent);
\r
5687 analysisTimerEvent = 0;
\r
5690 case DELAYED_TIMER_ID:
\r
5691 KillTimer(hwnd, delayedTimerEvent);
\r
5692 delayedTimerEvent = 0;
\r
5693 delayedTimerCallback();
\r
5698 case WM_USER_Input:
\r
5699 InputEvent(hwnd, message, wParam, lParam);
\r
5702 /* [AS] Also move "attached" child windows */
\r
5703 case WM_WINDOWPOSCHANGING:
\r
5705 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5706 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5708 if( ((lpwp->flags & SWP_NOMOVE) == 0) /*&& ((lpwp->flags & SWP_NOSIZE) != 0)*/ ) { // [HGM] in Win8 size always accompanies move?
\r
5709 /* Window is moving */
\r
5712 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5713 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5714 rcMain.right = wpMain.x + wpMain.width;
\r
5715 rcMain.top = wpMain.y;
\r
5716 rcMain.bottom = wpMain.y + wpMain.height;
\r
5718 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5719 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5720 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5721 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5722 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5723 wpMain.x = lpwp->x;
\r
5724 wpMain.y = lpwp->y;
\r
5729 /* [AS] Snapping */
\r
5730 case WM_ENTERSIZEMOVE:
\r
5731 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5732 if (hwnd == hwndMain) {
\r
5733 doingSizing = TRUE;
\r
5736 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5740 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5741 if (hwnd == hwndMain) {
\r
5742 lastSizing = wParam;
\r
5747 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5748 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5750 case WM_EXITSIZEMOVE:
\r
5751 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5752 if (hwnd == hwndMain) {
\r
5754 doingSizing = FALSE;
\r
5755 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5756 GetClientRect(hwnd, &client);
\r
5757 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5759 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5761 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5764 case WM_DESTROY: /* message: window being destroyed */
\r
5765 PostQuitMessage(0);
\r
5769 if (hwnd == hwndMain) {
\r
5774 default: /* Passes it on if unprocessed */
\r
5775 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5782 /*---------------------------------------------------------------------------*\
\r
5784 * Misc utility routines
\r
5786 \*---------------------------------------------------------------------------*/
\r
5789 * Decent random number generator, at least not as bad as Windows
\r
5790 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5792 unsigned int randstate;
\r
5797 randstate = randstate * 1664525 + 1013904223;
\r
5798 return (int) randstate & 0x7fffffff;
\r
5802 mysrandom(unsigned int seed)
\r
5809 * returns TRUE if user selects a different color, FALSE otherwise
\r
5813 ChangeColor(HWND hwnd, COLORREF *which)
\r
5815 static BOOL firstTime = TRUE;
\r
5816 static DWORD customColors[16];
\r
5818 COLORREF newcolor;
\r
5823 /* Make initial colors in use available as custom colors */
\r
5824 /* Should we put the compiled-in defaults here instead? */
\r
5826 customColors[i++] = lightSquareColor & 0xffffff;
\r
5827 customColors[i++] = darkSquareColor & 0xffffff;
\r
5828 customColors[i++] = whitePieceColor & 0xffffff;
\r
5829 customColors[i++] = blackPieceColor & 0xffffff;
\r
5830 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5831 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5833 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5834 customColors[i++] = textAttribs[ccl].color;
\r
5836 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5837 firstTime = FALSE;
\r
5840 cc.lStructSize = sizeof(cc);
\r
5841 cc.hwndOwner = hwnd;
\r
5842 cc.hInstance = NULL;
\r
5843 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5844 cc.lpCustColors = (LPDWORD) customColors;
\r
5845 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5847 if (!ChooseColor(&cc)) return FALSE;
\r
5849 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5850 if (newcolor == *which) return FALSE;
\r
5851 *which = newcolor;
\r
5855 InitDrawingColors();
\r
5856 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5861 MyLoadSound(MySound *ms)
\r
5867 if (ms->data && ms->flag) free(ms->data);
\r
5870 switch (ms->name[0]) {
\r
5876 /* System sound from Control Panel. Don't preload here. */
\r
5880 if (ms->name[1] == NULLCHAR) {
\r
5881 /* "!" alone = silence */
\r
5884 /* Builtin wave resource. Error if not found. */
\r
5885 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5886 if (h == NULL) break;
\r
5887 ms->data = (void *)LoadResource(hInst, h);
\r
5888 ms->flag = 0; // not maloced, so cannot be freed!
\r
5889 if (h == NULL) break;
\r
5894 /* .wav file. Error if not found. */
\r
5895 f = fopen(ms->name, "rb");
\r
5896 if (f == NULL) break;
\r
5897 if (fstat(fileno(f), &st) < 0) break;
\r
5898 ms->data = malloc(st.st_size);
\r
5900 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5906 char buf[MSG_SIZ];
\r
5907 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5908 DisplayError(buf, GetLastError());
\r
5914 MyPlaySound(MySound *ms)
\r
5916 BOOLEAN ok = FALSE;
\r
5918 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5919 switch (ms->name[0]) {
\r
5921 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5926 /* System sound from Control Panel (deprecated feature).
\r
5927 "$" alone or an unset sound name gets default beep (still in use). */
\r
5928 if (ms->name[1]) {
\r
5929 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5931 if (!ok) ok = MessageBeep(MB_OK);
\r
5934 /* Builtin wave resource, or "!" alone for silence */
\r
5935 if (ms->name[1]) {
\r
5936 if (ms->data == NULL) return FALSE;
\r
5937 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5943 /* .wav file. Error if not found. */
\r
5944 if (ms->data == NULL) return FALSE;
\r
5945 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5948 /* Don't print an error: this can happen innocently if the sound driver
\r
5949 is busy; for instance, if another instance of WinBoard is playing
\r
5950 a sound at about the same time. */
\r
5956 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5959 OPENFILENAME *ofn;
\r
5960 static UINT *number; /* gross that this is static */
\r
5962 switch (message) {
\r
5963 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5964 /* Center the dialog over the application window */
\r
5965 ofn = (OPENFILENAME *) lParam;
\r
5966 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5967 number = (UINT *) ofn->lCustData;
\r
5968 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5972 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5973 Translate(hDlg, 1536);
\r
5974 return FALSE; /* Allow for further processing */
\r
5977 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5978 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5980 return FALSE; /* Allow for further processing */
\r
5986 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5988 static UINT *number;
\r
5989 OPENFILENAME *ofname;
\r
5992 case WM_INITDIALOG:
\r
5993 Translate(hdlg, DLG_IndexNumber);
\r
5994 ofname = (OPENFILENAME *)lParam;
\r
5995 number = (UINT *)(ofname->lCustData);
\r
5998 ofnot = (OFNOTIFY *)lParam;
\r
5999 if (ofnot->hdr.code == CDN_FILEOK) {
\r
6000 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
6009 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
6010 char *nameFilt, char *dlgTitle, UINT *number,
\r
6011 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
6013 OPENFILENAME openFileName;
\r
6014 char buf1[MSG_SIZ];
\r
6017 if (fileName == NULL) fileName = buf1;
\r
6018 if (defName == NULL) {
\r
6019 safeStrCpy(fileName, "*.", 3 );
\r
6020 strcat(fileName, defExt);
\r
6022 safeStrCpy(fileName, defName, MSG_SIZ );
\r
6024 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
6025 if (number) *number = 0;
\r
6027 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
6028 openFileName.hwndOwner = hwnd;
\r
6029 openFileName.hInstance = (HANDLE) hInst;
\r
6030 openFileName.lpstrFilter = nameFilt;
\r
6031 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
6032 openFileName.nMaxCustFilter = 0L;
\r
6033 openFileName.nFilterIndex = 1L;
\r
6034 openFileName.lpstrFile = fileName;
\r
6035 openFileName.nMaxFile = MSG_SIZ;
\r
6036 openFileName.lpstrFileTitle = fileTitle;
\r
6037 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
6038 openFileName.lpstrInitialDir = NULL;
\r
6039 openFileName.lpstrTitle = dlgTitle;
\r
6040 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
6041 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
6042 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
6043 | (oldDialog ? 0 : OFN_EXPLORER);
\r
6044 openFileName.nFileOffset = 0;
\r
6045 openFileName.nFileExtension = 0;
\r
6046 openFileName.lpstrDefExt = defExt;
\r
6047 openFileName.lCustData = (LONG) number;
\r
6048 openFileName.lpfnHook = oldDialog ?
\r
6049 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
6050 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
6052 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
6053 GetOpenFileName(&openFileName)) {
\r
6054 /* open the file */
\r
6055 f = fopen(openFileName.lpstrFile, write);
\r
6057 MessageBox(hwnd, _("File open failed"), NULL,
\r
6058 MB_OK|MB_ICONEXCLAMATION);
\r
6062 int err = CommDlgExtendedError();
\r
6063 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
6072 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
6074 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
6077 * Get the first pop-up menu in the menu template. This is the
\r
6078 * menu that TrackPopupMenu displays.
\r
6080 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
6081 TranslateOneMenu(10, hmenuTrackPopup);
\r
6083 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
6086 * TrackPopup uses screen coordinates, so convert the
\r
6087 * coordinates of the mouse click to screen coordinates.
\r
6089 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
6091 /* Draw and track the floating pop-up menu. */
\r
6092 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
6093 pt.x, pt.y, 0, hwnd, NULL);
\r
6095 /* Destroy the menu.*/
\r
6096 DestroyMenu(hmenu);
\r
6101 int sizeX, sizeY, newSizeX, newSizeY;
\r
6103 } ResizeEditPlusButtonsClosure;
\r
6106 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
6108 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
6112 if (hChild == cl->hText) return TRUE;
\r
6113 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
6114 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
6115 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
6116 ScreenToClient(cl->hDlg, &pt);
\r
6117 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
6118 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
6122 /* Resize a dialog that has a (rich) edit field filling most of
\r
6123 the top, with a row of buttons below */
\r
6125 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
6128 int newTextHeight, newTextWidth;
\r
6129 ResizeEditPlusButtonsClosure cl;
\r
6131 /*if (IsIconic(hDlg)) return;*/
\r
6132 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
6134 cl.hdwp = BeginDeferWindowPos(8);
\r
6136 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
6137 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6138 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6139 if (newTextHeight < 0) {
\r
6140 newSizeY += -newTextHeight;
\r
6141 newTextHeight = 0;
\r
6143 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
6144 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6150 cl.newSizeX = newSizeX;
\r
6151 cl.newSizeY = newSizeY;
\r
6152 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
6154 EndDeferWindowPos(cl.hdwp);
\r
6157 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
6159 RECT rChild, rParent;
\r
6160 int wChild, hChild, wParent, hParent;
\r
6161 int wScreen, hScreen, xNew, yNew;
\r
6164 /* Get the Height and Width of the child window */
\r
6165 GetWindowRect (hwndChild, &rChild);
\r
6166 wChild = rChild.right - rChild.left;
\r
6167 hChild = rChild.bottom - rChild.top;
\r
6169 /* Get the Height and Width of the parent window */
\r
6170 GetWindowRect (hwndParent, &rParent);
\r
6171 wParent = rParent.right - rParent.left;
\r
6172 hParent = rParent.bottom - rParent.top;
\r
6174 /* Get the display limits */
\r
6175 hdc = GetDC (hwndChild);
\r
6176 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
6177 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
6178 ReleaseDC(hwndChild, hdc);
\r
6180 /* Calculate new X position, then adjust for screen */
\r
6181 xNew = rParent.left + ((wParent - wChild) /2);
\r
6184 } else if ((xNew+wChild) > wScreen) {
\r
6185 xNew = wScreen - wChild;
\r
6188 /* Calculate new Y position, then adjust for screen */
\r
6190 yNew = rParent.top + ((hParent - hChild) /2);
\r
6193 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
6198 } else if ((yNew+hChild) > hScreen) {
\r
6199 yNew = hScreen - hChild;
\r
6202 /* Set it, and return */
\r
6203 return SetWindowPos (hwndChild, NULL,
\r
6204 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
6207 /* Center one window over another */
\r
6208 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
6210 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
6213 /*---------------------------------------------------------------------------*\
\r
6215 * Startup Dialog functions
\r
6217 \*---------------------------------------------------------------------------*/
\r
6219 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
6221 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6223 while (*cd != NULL) {
\r
6224 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
6230 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
6232 char buf1[MAX_ARG_LEN];
\r
6235 if (str[0] == '@') {
\r
6236 FILE* f = fopen(str + 1, "r");
\r
6238 DisplayFatalError(str + 1, errno, 2);
\r
6241 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
6243 buf1[len] = NULLCHAR;
\r
6247 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6250 char buf[MSG_SIZ];
\r
6251 char *end = strchr(str, '\n');
\r
6252 if (end == NULL) return;
\r
6253 memcpy(buf, str, end - str);
\r
6254 buf[end - str] = NULLCHAR;
\r
6255 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6261 SetStartupDialogEnables(HWND hDlg)
\r
6263 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6264 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6265 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6266 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6267 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6268 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6269 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6270 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6271 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6272 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6273 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6274 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6275 IsDlgButtonChecked(hDlg, OPT_View));
\r
6279 QuoteForFilename(char *filename)
\r
6281 int dquote, space;
\r
6282 dquote = strchr(filename, '"') != NULL;
\r
6283 space = strchr(filename, ' ') != NULL;
\r
6284 if (dquote || space) {
\r
6296 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6298 char buf[MSG_SIZ];
\r
6301 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6302 q = QuoteForFilename(nthcp);
\r
6303 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6304 if (*nthdir != NULLCHAR) {
\r
6305 q = QuoteForFilename(nthdir);
\r
6306 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6308 if (*nthcp == NULLCHAR) {
\r
6309 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6310 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6311 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6312 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6317 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6319 char buf[MSG_SIZ];
\r
6323 switch (message) {
\r
6324 case WM_INITDIALOG:
\r
6325 /* Center the dialog */
\r
6326 CenterWindow (hDlg, GetDesktopWindow());
\r
6327 Translate(hDlg, DLG_Startup);
\r
6328 /* Initialize the dialog items */
\r
6329 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6330 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6331 firstChessProgramNames);
\r
6332 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6333 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6334 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6335 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6336 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6337 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6338 if (*appData.icsHelper != NULLCHAR) {
\r
6339 char *q = QuoteForFilename(appData.icsHelper);
\r
6340 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6342 if (*appData.icsHost == NULLCHAR) {
\r
6343 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6344 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6345 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6346 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6347 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6350 if (appData.icsActive) {
\r
6351 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6353 else if (appData.noChessProgram) {
\r
6354 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6357 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6360 SetStartupDialogEnables(hDlg);
\r
6364 switch (LOWORD(wParam)) {
\r
6366 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6367 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6368 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6370 comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox
\r
6371 ParseArgs(StringGet, &p);
\r
6372 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6373 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6375 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6376 ParseArgs(StringGet, &p);
\r
6377 SwapEngines(singleList); // ... and then make it 'second'
\r
6379 appData.noChessProgram = FALSE;
\r
6380 appData.icsActive = FALSE;
\r
6381 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6382 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6383 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6385 ParseArgs(StringGet, &p);
\r
6386 if (appData.zippyPlay) {
\r
6387 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6388 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6390 ParseArgs(StringGet, &p);
\r
6392 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6393 appData.noChessProgram = TRUE;
\r
6394 appData.icsActive = FALSE;
\r
6396 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6397 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6400 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6401 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6403 ParseArgs(StringGet, &p);
\r
6405 EndDialog(hDlg, TRUE);
\r
6412 case IDM_HELPCONTENTS:
\r
6413 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6414 MessageBox (GetFocus(),
\r
6415 _("Unable to activate help"),
\r
6416 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6421 SetStartupDialogEnables(hDlg);
\r
6429 /*---------------------------------------------------------------------------*\
\r
6431 * About box dialog functions
\r
6433 \*---------------------------------------------------------------------------*/
\r
6435 /* Process messages for "About" dialog box */
\r
6437 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6439 switch (message) {
\r
6440 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6441 /* Center the dialog over the application window */
\r
6442 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6443 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6444 Translate(hDlg, ABOUTBOX);
\r
6448 case WM_COMMAND: /* message: received a command */
\r
6449 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6450 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6451 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6459 /*---------------------------------------------------------------------------*\
\r
6461 * Comment Dialog functions
\r
6463 \*---------------------------------------------------------------------------*/
\r
6466 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6468 static HANDLE hwndText = NULL;
\r
6469 int len, newSizeX, newSizeY;
\r
6470 static int sizeX, sizeY;
\r
6475 switch (message) {
\r
6476 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6477 /* Initialize the dialog items */
\r
6478 Translate(hDlg, DLG_EditComment);
\r
6479 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6480 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6481 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6482 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6483 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6484 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6485 SetWindowText(hDlg, commentTitle);
\r
6486 if (editComment) {
\r
6487 SetFocus(hwndText);
\r
6489 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6491 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6492 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6493 MAKELPARAM(FALSE, 0));
\r
6494 /* Size and position the dialog */
\r
6495 if (!commentDialog) {
\r
6496 commentDialog = hDlg;
\r
6497 GetClientRect(hDlg, &rect);
\r
6498 sizeX = rect.right;
\r
6499 sizeY = rect.bottom;
\r
6500 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6501 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6502 WINDOWPLACEMENT wp;
\r
6503 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6504 wp.length = sizeof(WINDOWPLACEMENT);
\r
6506 wp.showCmd = SW_SHOW;
\r
6507 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6508 wp.rcNormalPosition.left = wpComment.x;
\r
6509 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6510 wp.rcNormalPosition.top = wpComment.y;
\r
6511 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6512 SetWindowPlacement(hDlg, &wp);
\r
6514 GetClientRect(hDlg, &rect);
\r
6515 newSizeX = rect.right;
\r
6516 newSizeY = rect.bottom;
\r
6517 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6518 newSizeX, newSizeY);
\r
6523 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6526 case WM_COMMAND: /* message: received a command */
\r
6527 switch (LOWORD(wParam)) {
\r
6529 if (editComment) {
\r
6531 /* Read changed options from the dialog box */
\r
6532 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6533 len = GetWindowTextLength(hwndText);
\r
6534 str = (char *) malloc(len + 1);
\r
6535 GetWindowText(hwndText, str, len + 1);
\r
6544 ReplaceComment(commentIndex, str);
\r
6551 case OPT_CancelComment:
\r
6555 case OPT_ClearComment:
\r
6556 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6559 case OPT_EditComment:
\r
6560 EditCommentEvent();
\r
6568 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6569 if( wParam == OPT_CommentText ) {
\r
6570 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6572 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6573 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6577 pt.x = LOWORD( lpMF->lParam );
\r
6578 pt.y = HIWORD( lpMF->lParam );
\r
6580 if(lpMF->msg == WM_CHAR) {
\r
6582 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6583 index = sel.cpMin;
\r
6585 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6587 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6588 len = GetWindowTextLength(hwndText);
\r
6589 str = (char *) malloc(len + 1);
\r
6590 GetWindowText(hwndText, str, len + 1);
\r
6591 ReplaceComment(commentIndex, str);
\r
6592 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6593 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6596 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6597 lpMF->msg = WM_USER;
\r
6605 newSizeX = LOWORD(lParam);
\r
6606 newSizeY = HIWORD(lParam);
\r
6607 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6612 case WM_GETMINMAXINFO:
\r
6613 /* Prevent resizing window too small */
\r
6614 mmi = (MINMAXINFO *) lParam;
\r
6615 mmi->ptMinTrackSize.x = 100;
\r
6616 mmi->ptMinTrackSize.y = 100;
\r
6623 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6628 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6630 if (str == NULL) str = "";
\r
6631 p = (char *) malloc(2 * strlen(str) + 2);
\r
6634 if (*str == '\n') *q++ = '\r';
\r
6638 if (commentText != NULL) free(commentText);
\r
6640 commentIndex = index;
\r
6641 commentTitle = title;
\r
6643 editComment = edit;
\r
6645 if (commentDialog) {
\r
6646 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6647 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6649 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6650 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6651 hwndMain, (DLGPROC)lpProc);
\r
6652 FreeProcInstance(lpProc);
\r
6658 /*---------------------------------------------------------------------------*\
\r
6660 * Type-in move dialog functions
\r
6662 \*---------------------------------------------------------------------------*/
\r
6665 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6667 char move[MSG_SIZ];
\r
6670 switch (message) {
\r
6671 case WM_INITDIALOG:
\r
6672 move[0] = (char) lParam;
\r
6673 move[1] = NULLCHAR;
\r
6674 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6675 Translate(hDlg, DLG_TypeInMove);
\r
6676 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6677 SetWindowText(hInput, move);
\r
6679 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6683 switch (LOWORD(wParam)) {
\r
6686 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6687 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6688 TypeInDoneEvent(move);
\r
6689 EndDialog(hDlg, TRUE);
\r
6692 EndDialog(hDlg, FALSE);
\r
6703 PopUpMoveDialog(char firstchar)
\r
6707 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6708 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6709 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6710 FreeProcInstance(lpProc);
\r
6713 /*---------------------------------------------------------------------------*\
\r
6715 * Type-in name dialog functions
\r
6717 \*---------------------------------------------------------------------------*/
\r
6720 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6722 char move[MSG_SIZ];
\r
6725 switch (message) {
\r
6726 case WM_INITDIALOG:
\r
6727 move[0] = (char) lParam;
\r
6728 move[1] = NULLCHAR;
\r
6729 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6730 Translate(hDlg, DLG_TypeInName);
\r
6731 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6732 SetWindowText(hInput, move);
\r
6734 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6738 switch (LOWORD(wParam)) {
\r
6740 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6741 appData.userName = strdup(move);
\r
6742 SetUserLogo(); DisplayLogos();
\r
6744 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6745 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6746 DisplayTitle(move);
\r
6750 EndDialog(hDlg, TRUE);
\r
6753 EndDialog(hDlg, FALSE);
\r
6764 PopUpNameDialog(char firstchar)
\r
6768 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6769 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6770 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6771 FreeProcInstance(lpProc);
\r
6774 /*---------------------------------------------------------------------------*\
\r
6778 \*---------------------------------------------------------------------------*/
\r
6780 /* Nonmodal error box */
\r
6781 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6782 WPARAM wParam, LPARAM lParam);
\r
6785 ErrorPopUp(char *title, char *content)
\r
6789 BOOLEAN modal = hwndMain == NULL;
\r
6807 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6808 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6811 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6813 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6814 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6815 hwndMain, (DLGPROC)lpProc);
\r
6816 FreeProcInstance(lpProc);
\r
6823 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6824 if (errorDialog == NULL) return;
\r
6825 DestroyWindow(errorDialog);
\r
6826 errorDialog = NULL;
\r
6827 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6831 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6835 switch (message) {
\r
6836 case WM_INITDIALOG:
\r
6837 GetWindowRect(hDlg, &rChild);
\r
6840 SetWindowPos(hDlg, NULL, rChild.left,
\r
6841 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6842 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6846 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6847 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6848 and it doesn't work when you resize the dialog.
\r
6849 For now, just give it a default position.
\r
6851 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6852 Translate(hDlg, DLG_Error);
\r
6854 errorDialog = hDlg;
\r
6855 SetWindowText(hDlg, errorTitle);
\r
6856 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6860 switch (LOWORD(wParam)) {
\r
6863 if (errorDialog == hDlg) errorDialog = NULL;
\r
6864 DestroyWindow(hDlg);
\r
6876 HWND gothicDialog = NULL;
\r
6879 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6882 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6884 switch (message) {
\r
6885 case WM_INITDIALOG:
\r
6886 GetWindowRect(hDlg, &rChild);
\r
6888 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6892 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6893 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6894 and it doesn't work when you resize the dialog.
\r
6895 For now, just give it a default position.
\r
6897 gothicDialog = hDlg;
\r
6898 SetWindowText(hDlg, errorTitle);
\r
6899 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6903 switch (LOWORD(wParam)) {
\r
6906 if (errorDialog == hDlg) errorDialog = NULL;
\r
6907 DestroyWindow(hDlg);
\r
6919 GothicPopUp(char *title, VariantClass variant)
\r
6922 static char *lastTitle;
\r
6924 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6925 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6927 if(lastTitle != title && gothicDialog != NULL) {
\r
6928 DestroyWindow(gothicDialog);
\r
6929 gothicDialog = NULL;
\r
6931 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6932 title = lastTitle;
\r
6933 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6934 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6935 hwndMain, (DLGPROC)lpProc);
\r
6936 FreeProcInstance(lpProc);
\r
6941 /*---------------------------------------------------------------------------*\
\r
6943 * Ics Interaction console functions
\r
6945 \*---------------------------------------------------------------------------*/
\r
6947 #define HISTORY_SIZE 64
\r
6948 static char *history[HISTORY_SIZE];
\r
6949 int histIn = 0, histP = 0;
\r
6953 SaveInHistory(char *cmd)
\r
6955 if (history[histIn] != NULL) {
\r
6956 free(history[histIn]);
\r
6957 history[histIn] = NULL;
\r
6959 if (*cmd == NULLCHAR) return;
\r
6960 history[histIn] = StrSave(cmd);
\r
6961 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6962 if (history[histIn] != NULL) {
\r
6963 free(history[histIn]);
\r
6965 history[histIn] = NULL;
\r
6971 PrevInHistory(char *cmd)
\r
6974 if (histP == histIn) {
\r
6975 if (history[histIn] != NULL) free(history[histIn]);
\r
6976 history[histIn] = StrSave(cmd);
\r
6978 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6979 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6981 return history[histP];
\r
6987 if (histP == histIn) return NULL;
\r
6988 histP = (histP + 1) % HISTORY_SIZE;
\r
6989 return history[histP];
\r
6993 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6997 hmenu = LoadMenu(hInst, "TextMenu");
\r
6998 h = GetSubMenu(hmenu, 0);
\r
7000 if (strcmp(e->item, "-") == 0) {
\r
7001 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
7002 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
7003 int flags = MF_STRING, j = 0;
\r
7004 if (e->item[0] == '|') {
\r
7005 flags |= MF_MENUBARBREAK;
\r
7008 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
7009 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
7017 WNDPROC consoleTextWindowProc;
\r
7020 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
7022 char buf[MSG_SIZ], name[MSG_SIZ];
\r
7023 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7027 SetWindowText(hInput, command);
\r
7029 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
7031 sel.cpMin = 999999;
\r
7032 sel.cpMax = 999999;
\r
7033 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7038 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7039 if (sel.cpMin == sel.cpMax) {
\r
7040 /* Expand to surrounding word */
\r
7043 tr.chrg.cpMax = sel.cpMin;
\r
7044 tr.chrg.cpMin = --sel.cpMin;
\r
7045 if (sel.cpMin < 0) break;
\r
7046 tr.lpstrText = name;
\r
7047 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
7048 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
7052 tr.chrg.cpMin = sel.cpMax;
\r
7053 tr.chrg.cpMax = ++sel.cpMax;
\r
7054 tr.lpstrText = name;
\r
7055 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
7056 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
7059 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
7060 MessageBeep(MB_ICONEXCLAMATION);
\r
7064 tr.lpstrText = name;
\r
7065 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
7067 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
7068 MessageBeep(MB_ICONEXCLAMATION);
\r
7071 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
7074 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
7075 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
7076 SetWindowText(hInput, buf);
\r
7077 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
7079 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
7080 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
7081 SetWindowText(hInput, buf);
\r
7082 sel.cpMin = 999999;
\r
7083 sel.cpMax = 999999;
\r
7084 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7090 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7095 switch (message) {
\r
7097 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7098 if(wParam=='R') return 0;
\r
7101 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
7104 sel.cpMin = 999999;
\r
7105 sel.cpMax = 999999;
\r
7106 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7107 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
7112 if(wParam != '\022') {
\r
7113 if (wParam == '\t') {
\r
7114 if (GetKeyState(VK_SHIFT) < 0) {
\r
7116 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7117 if (buttonDesc[0].hwnd) {
\r
7118 SetFocus(buttonDesc[0].hwnd);
\r
7120 SetFocus(hwndMain);
\r
7124 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
7127 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7128 JAWS_DELETE( SetFocus(hInput); )
\r
7129 SendMessage(hInput, message, wParam, lParam);
\r
7132 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
7134 case WM_RBUTTONDOWN:
\r
7135 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
7136 /* Move selection here if it was empty */
\r
7138 pt.x = LOWORD(lParam);
\r
7139 pt.y = HIWORD(lParam);
\r
7140 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7141 if (sel.cpMin == sel.cpMax) {
\r
7142 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
7143 sel.cpMax = sel.cpMin;
\r
7144 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7146 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
7147 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
7149 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
7150 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7151 if (sel.cpMin == sel.cpMax) {
\r
7152 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7153 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
7155 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7156 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7158 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
7159 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
7160 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
7161 MenuPopup(hwnd, pt, hmenu, -1);
\r
7165 case WM_RBUTTONUP:
\r
7166 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7167 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7168 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7172 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7174 return SendMessage(hInput, message, wParam, lParam);
\r
7175 case WM_MBUTTONDOWN:
\r
7176 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7178 switch (LOWORD(wParam)) {
\r
7179 case IDM_QuickPaste:
\r
7181 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7182 if (sel.cpMin == sel.cpMax) {
\r
7183 MessageBeep(MB_ICONEXCLAMATION);
\r
7186 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7187 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7188 SendMessage(hInput, WM_PASTE, 0, 0);
\r
7193 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7196 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7199 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7203 int i = LOWORD(wParam) - IDM_CommandX;
\r
7204 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
7205 icsTextMenuEntry[i].command != NULL) {
\r
7206 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
7207 icsTextMenuEntry[i].getname,
\r
7208 icsTextMenuEntry[i].immediate);
\r
7216 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
7219 WNDPROC consoleInputWindowProc;
\r
7222 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7224 char buf[MSG_SIZ];
\r
7226 static BOOL sendNextChar = FALSE;
\r
7227 static BOOL quoteNextChar = FALSE;
\r
7228 InputSource *is = consoleInputSource;
\r
7232 switch (message) {
\r
7234 if (!appData.localLineEditing || sendNextChar) {
\r
7235 is->buf[0] = (CHAR) wParam;
\r
7237 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7238 sendNextChar = FALSE;
\r
7241 if (quoteNextChar) {
\r
7242 buf[0] = (char) wParam;
\r
7243 buf[1] = NULLCHAR;
\r
7244 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7245 quoteNextChar = FALSE;
\r
7249 case '\r': /* Enter key */
\r
7250 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7251 if (consoleEcho) SaveInHistory(is->buf);
\r
7252 is->buf[is->count++] = '\n';
\r
7253 is->buf[is->count] = NULLCHAR;
\r
7254 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7255 if (consoleEcho) {
\r
7256 ConsoleOutput(is->buf, is->count, TRUE);
\r
7257 } else if (appData.localLineEditing) {
\r
7258 ConsoleOutput("\n", 1, TRUE);
\r
7261 case '\033': /* Escape key */
\r
7262 SetWindowText(hwnd, "");
\r
7263 cf.cbSize = sizeof(CHARFORMAT);
\r
7264 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7265 if (consoleEcho) {
\r
7266 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7268 cf.crTextColor = COLOR_ECHOOFF;
\r
7270 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7271 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7273 case '\t': /* Tab key */
\r
7274 if (GetKeyState(VK_SHIFT) < 0) {
\r
7276 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7279 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7280 if (buttonDesc[0].hwnd) {
\r
7281 SetFocus(buttonDesc[0].hwnd);
\r
7283 SetFocus(hwndMain);
\r
7287 case '\023': /* Ctrl+S */
\r
7288 sendNextChar = TRUE;
\r
7290 case '\021': /* Ctrl+Q */
\r
7291 quoteNextChar = TRUE;
\r
7301 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7302 p = PrevInHistory(buf);
\r
7304 SetWindowText(hwnd, p);
\r
7305 sel.cpMin = 999999;
\r
7306 sel.cpMax = 999999;
\r
7307 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7312 p = NextInHistory();
\r
7314 SetWindowText(hwnd, p);
\r
7315 sel.cpMin = 999999;
\r
7316 sel.cpMax = 999999;
\r
7317 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7323 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7327 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7331 case WM_MBUTTONDOWN:
\r
7332 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7333 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7335 case WM_RBUTTONUP:
\r
7336 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7337 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7338 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7342 hmenu = LoadMenu(hInst, "InputMenu");
\r
7343 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7344 if (sel.cpMin == sel.cpMax) {
\r
7345 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7346 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7348 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7349 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7351 pt.x = LOWORD(lParam);
\r
7352 pt.y = HIWORD(lParam);
\r
7353 MenuPopup(hwnd, pt, hmenu, -1);
\r
7357 switch (LOWORD(wParam)) {
\r
7359 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7361 case IDM_SelectAll:
\r
7363 sel.cpMax = -1; /*999999?*/
\r
7364 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7367 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7370 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7373 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7378 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7381 #define CO_MAX 100000
\r
7382 #define CO_TRIM 1000
\r
7385 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7387 static SnapData sd;
\r
7388 HWND hText, hInput;
\r
7390 static int sizeX, sizeY;
\r
7391 int newSizeX, newSizeY;
\r
7395 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7396 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7398 switch (message) {
\r
7400 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7402 ENLINK *pLink = (ENLINK*)lParam;
\r
7403 if (pLink->msg == WM_LBUTTONUP)
\r
7407 tr.chrg = pLink->chrg;
\r
7408 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7409 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7410 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7411 free(tr.lpstrText);
\r
7415 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7416 hwndConsole = hDlg;
\r
7418 consoleTextWindowProc = (WNDPROC)
\r
7419 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7420 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7421 consoleInputWindowProc = (WNDPROC)
\r
7422 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7423 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7424 Colorize(ColorNormal, TRUE);
\r
7425 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7426 ChangedConsoleFont();
\r
7427 GetClientRect(hDlg, &rect);
\r
7428 sizeX = rect.right;
\r
7429 sizeY = rect.bottom;
\r
7430 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7431 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7432 WINDOWPLACEMENT wp;
\r
7433 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7434 wp.length = sizeof(WINDOWPLACEMENT);
\r
7436 wp.showCmd = SW_SHOW;
\r
7437 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7438 wp.rcNormalPosition.left = wpConsole.x;
\r
7439 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7440 wp.rcNormalPosition.top = wpConsole.y;
\r
7441 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7442 SetWindowPlacement(hDlg, &wp);
\r
7445 // [HGM] Chessknight's change 2004-07-13
\r
7446 else { /* Determine Defaults */
\r
7447 WINDOWPLACEMENT wp;
\r
7448 wpConsole.x = wpMain.width + 1;
\r
7449 wpConsole.y = wpMain.y;
\r
7450 wpConsole.width = screenWidth - wpMain.width;
\r
7451 wpConsole.height = wpMain.height;
\r
7452 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7453 wp.length = sizeof(WINDOWPLACEMENT);
\r
7455 wp.showCmd = SW_SHOW;
\r
7456 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7457 wp.rcNormalPosition.left = wpConsole.x;
\r
7458 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7459 wp.rcNormalPosition.top = wpConsole.y;
\r
7460 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7461 SetWindowPlacement(hDlg, &wp);
\r
7464 // Allow hText to highlight URLs and send notifications on them
\r
7465 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7466 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7467 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7468 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7482 if (IsIconic(hDlg)) break;
\r
7483 newSizeX = LOWORD(lParam);
\r
7484 newSizeY = HIWORD(lParam);
\r
7485 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7486 RECT rectText, rectInput;
\r
7488 int newTextHeight, newTextWidth;
\r
7489 GetWindowRect(hText, &rectText);
\r
7490 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7491 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7492 if (newTextHeight < 0) {
\r
7493 newSizeY += -newTextHeight;
\r
7494 newTextHeight = 0;
\r
7496 SetWindowPos(hText, NULL, 0, 0,
\r
7497 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7498 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7499 pt.x = rectInput.left;
\r
7500 pt.y = rectInput.top + newSizeY - sizeY;
\r
7501 ScreenToClient(hDlg, &pt);
\r
7502 SetWindowPos(hInput, NULL,
\r
7503 pt.x, pt.y, /* needs client coords */
\r
7504 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7505 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7511 case WM_GETMINMAXINFO:
\r
7512 /* Prevent resizing window too small */
\r
7513 mmi = (MINMAXINFO *) lParam;
\r
7514 mmi->ptMinTrackSize.x = 100;
\r
7515 mmi->ptMinTrackSize.y = 100;
\r
7518 /* [AS] Snapping */
\r
7519 case WM_ENTERSIZEMOVE:
\r
7520 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7523 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7526 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7528 case WM_EXITSIZEMOVE:
\r
7529 UpdateICSWidth(hText);
\r
7530 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7533 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7541 if (hwndConsole) return;
\r
7542 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7543 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7548 ConsoleOutput(char* data, int length, int forceVisible)
\r
7553 char buf[CO_MAX+1];
\r
7556 static int delayLF = 0;
\r
7557 CHARRANGE savesel, sel;
\r
7559 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7567 while (length--) {
\r
7575 } else if (*p == '\007') {
\r
7576 MyPlaySound(&sounds[(int)SoundBell]);
\r
7583 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7584 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7585 /* Save current selection */
\r
7586 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7587 exlen = GetWindowTextLength(hText);
\r
7588 /* Find out whether current end of text is visible */
\r
7589 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7590 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7591 /* Trim existing text if it's too long */
\r
7592 if (exlen + (q - buf) > CO_MAX) {
\r
7593 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7596 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7597 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7599 savesel.cpMin -= trim;
\r
7600 savesel.cpMax -= trim;
\r
7601 if (exlen < 0) exlen = 0;
\r
7602 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7603 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7605 /* Append the new text */
\r
7606 sel.cpMin = exlen;
\r
7607 sel.cpMax = exlen;
\r
7608 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7609 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7610 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7611 if (forceVisible || exlen == 0 ||
\r
7612 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7613 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7614 /* Scroll to make new end of text visible if old end of text
\r
7615 was visible or new text is an echo of user typein */
\r
7616 sel.cpMin = 9999999;
\r
7617 sel.cpMax = 9999999;
\r
7618 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7619 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7620 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7621 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7623 if (savesel.cpMax == exlen || forceVisible) {
\r
7624 /* Move insert point to new end of text if it was at the old
\r
7625 end of text or if the new text is an echo of user typein */
\r
7626 sel.cpMin = 9999999;
\r
7627 sel.cpMax = 9999999;
\r
7628 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7630 /* Restore previous selection */
\r
7631 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7633 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7640 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7644 COLORREF oldFg, oldBg;
\r
7648 if(copyNumber > 1)
\r
7649 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7651 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7652 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7653 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7656 rect.right = x + squareSize;
\r
7658 rect.bottom = y + squareSize;
\r
7661 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7662 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7663 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7664 &rect, str, strlen(str), NULL);
\r
7666 (void) SetTextColor(hdc, oldFg);
\r
7667 (void) SetBkColor(hdc, oldBg);
\r
7668 (void) SelectObject(hdc, oldFont);
\r
7672 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7673 RECT *rect, char *color, char *flagFell)
\r
7677 COLORREF oldFg, oldBg;
\r
7680 if (twoBoards && partnerUp) return;
\r
7681 if (appData.clockMode) {
\r
7682 if (tinyLayout == 2)
\r
7683 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7685 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7692 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7693 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7695 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7696 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7699 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7703 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7704 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7705 rect, str, strlen(str), NULL);
\r
7706 if(logoHeight > 0 && appData.clockMode) {
\r
7708 str += strlen(color)+2;
\r
7709 r.top = rect->top + logoHeight/2;
\r
7710 r.left = rect->left;
\r
7711 r.right = rect->right;
\r
7712 r.bottom = rect->bottom;
\r
7713 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7714 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7715 &r, str, strlen(str), NULL);
\r
7717 (void) SetTextColor(hdc, oldFg);
\r
7718 (void) SetBkColor(hdc, oldBg);
\r
7719 (void) SelectObject(hdc, oldFont);
\r
7724 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7730 if( count <= 0 ) {
\r
7731 if (appData.debugMode) {
\r
7732 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7735 return ERROR_INVALID_USER_BUFFER;
\r
7738 ResetEvent(ovl->hEvent);
\r
7739 ovl->Offset = ovl->OffsetHigh = 0;
\r
7740 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7744 err = GetLastError();
\r
7745 if (err == ERROR_IO_PENDING) {
\r
7746 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7750 err = GetLastError();
\r
7757 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7762 ResetEvent(ovl->hEvent);
\r
7763 ovl->Offset = ovl->OffsetHigh = 0;
\r
7764 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7768 err = GetLastError();
\r
7769 if (err == ERROR_IO_PENDING) {
\r
7770 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7774 err = GetLastError();
\r
7781 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7782 void CheckForInputBufferFull( InputSource * is )
\r
7784 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7785 /* Look for end of line */
\r
7786 char * p = is->buf;
\r
7788 while( p < is->next && *p != '\n' ) {
\r
7792 if( p >= is->next ) {
\r
7793 if (appData.debugMode) {
\r
7794 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7797 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7798 is->count = (DWORD) -1;
\r
7799 is->next = is->buf;
\r
7805 InputThread(LPVOID arg)
\r
7810 is = (InputSource *) arg;
\r
7811 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7812 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7813 while (is->hThread != NULL) {
\r
7814 is->error = DoReadFile(is->hFile, is->next,
\r
7815 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7816 &is->count, &ovl);
\r
7817 if (is->error == NO_ERROR) {
\r
7818 is->next += is->count;
\r
7820 if (is->error == ERROR_BROKEN_PIPE) {
\r
7821 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7824 is->count = (DWORD) -1;
\r
7825 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7830 CheckForInputBufferFull( is );
\r
7832 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7834 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7836 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7839 CloseHandle(ovl.hEvent);
\r
7840 CloseHandle(is->hFile);
\r
7842 if (appData.debugMode) {
\r
7843 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7850 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7852 NonOvlInputThread(LPVOID arg)
\r
7859 is = (InputSource *) arg;
\r
7860 while (is->hThread != NULL) {
\r
7861 is->error = ReadFile(is->hFile, is->next,
\r
7862 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7863 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7864 if (is->error == NO_ERROR) {
\r
7865 /* Change CRLF to LF */
\r
7866 if (is->next > is->buf) {
\r
7868 i = is->count + 1;
\r
7876 if (prev == '\r' && *p == '\n') {
\r
7888 if (is->error == ERROR_BROKEN_PIPE) {
\r
7889 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7892 is->count = (DWORD) -1;
\r
7896 CheckForInputBufferFull( is );
\r
7898 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7900 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7902 if (is->count < 0) break; /* Quit on error */
\r
7904 CloseHandle(is->hFile);
\r
7909 SocketInputThread(LPVOID arg)
\r
7913 is = (InputSource *) arg;
\r
7914 while (is->hThread != NULL) {
\r
7915 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7916 if ((int)is->count == SOCKET_ERROR) {
\r
7917 is->count = (DWORD) -1;
\r
7918 is->error = WSAGetLastError();
\r
7920 is->error = NO_ERROR;
\r
7921 is->next += is->count;
\r
7922 if (is->count == 0 && is->second == is) {
\r
7923 /* End of file on stderr; quit with no message */
\r
7927 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7929 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7931 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7937 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7941 is = (InputSource *) lParam;
\r
7942 if (is->lineByLine) {
\r
7943 /* Feed in lines one by one */
\r
7944 char *p = is->buf;
\r
7946 while (q < is->next) {
\r
7947 if (*q++ == '\n') {
\r
7948 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7953 /* Move any partial line to the start of the buffer */
\r
7955 while (p < is->next) {
\r
7960 if (is->error != NO_ERROR || is->count == 0) {
\r
7961 /* Notify backend of the error. Note: If there was a partial
\r
7962 line at the end, it is not flushed through. */
\r
7963 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7966 /* Feed in the whole chunk of input at once */
\r
7967 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7968 is->next = is->buf;
\r
7972 /*---------------------------------------------------------------------------*\
\r
7974 * Menu enables. Used when setting various modes.
\r
7976 \*---------------------------------------------------------------------------*/
\r
7984 GreyRevert(Boolean grey)
\r
7985 { // [HGM] vari: for retracting variations in local mode
\r
7986 HMENU hmenu = GetMenu(hwndMain);
\r
7987 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7988 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7992 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7994 while (enab->item > 0) {
\r
7995 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
8000 Enables gnuEnables[] = {
\r
8001 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
8002 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
8003 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
8004 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
8005 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
8006 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
8007 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
8008 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
8009 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
8010 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
8011 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
8012 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
8013 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
8016 // Needed to switch from ncp to GNU mode on Engine Load
\r
8017 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
8018 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
8019 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
8020 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
8021 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
8022 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
8023 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
8024 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
8025 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
8026 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
8027 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
8028 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8029 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
8030 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
8034 Enables icsEnables[] = {
\r
8035 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
8036 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
8037 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8038 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8039 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8040 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8041 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
8042 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
8043 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
8044 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
8045 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8046 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
8047 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
8048 { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },
\r
8049 { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },
\r
8050 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
8051 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
8052 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
8053 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
8054 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
8059 Enables zippyEnables[] = {
\r
8060 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8061 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
8062 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
8063 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
8068 Enables ncpEnables[] = {
\r
8069 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
8070 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
8071 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8072 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8073 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8074 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8075 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
8076 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
8077 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
8078 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
8079 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
8080 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
8081 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8082 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
8083 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
8084 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
8085 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
8086 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
8087 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
8088 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
8089 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
8090 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
8094 Enables trainingOnEnables[] = {
\r
8095 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
8096 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
8097 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
8098 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
8099 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
8100 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
8101 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
8102 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8103 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
8107 Enables trainingOffEnables[] = {
\r
8108 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
8109 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
8110 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
8111 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
8112 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
8113 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
8114 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
8115 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8116 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
8120 /* These modify either ncpEnables or gnuEnables */
\r
8121 Enables cmailEnables[] = {
\r
8122 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
8123 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
8124 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
8125 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
8126 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
8127 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
8128 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
8132 Enables machineThinkingEnables[] = {
\r
8133 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8134 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
8135 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
8136 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8137 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
8138 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8139 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8140 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8141 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8142 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
8143 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8144 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8145 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8146 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8147 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
8148 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
8152 Enables userThinkingEnables[] = {
\r
8153 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8154 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
8155 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
8156 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8157 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
8158 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8159 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8160 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8161 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8162 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
8163 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
8164 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
8165 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
8166 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
8167 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
8168 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
8172 /*---------------------------------------------------------------------------*\
\r
8174 * Front-end interface functions exported by XBoard.
\r
8175 * Functions appear in same order as prototypes in frontend.h.
\r
8177 \*---------------------------------------------------------------------------*/
\r
8179 CheckMark(UINT item, int state)
\r
8181 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
8187 static UINT prevChecked = 0;
\r
8188 static int prevPausing = 0;
\r
8191 if (pausing != prevPausing) {
\r
8192 prevPausing = pausing;
\r
8193 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
8194 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
8195 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
8198 switch (gameMode) {
\r
8199 case BeginningOfGame:
\r
8200 if (appData.icsActive)
\r
8201 nowChecked = IDM_IcsClient;
\r
8202 else if (appData.noChessProgram)
\r
8203 nowChecked = IDM_EditGame;
\r
8205 nowChecked = IDM_MachineBlack;
\r
8207 case MachinePlaysBlack:
\r
8208 nowChecked = IDM_MachineBlack;
\r
8210 case MachinePlaysWhite:
\r
8211 nowChecked = IDM_MachineWhite;
\r
8213 case TwoMachinesPlay:
\r
8214 nowChecked = IDM_TwoMachines;
\r
8217 nowChecked = IDM_AnalysisMode;
\r
8220 nowChecked = IDM_AnalyzeFile;
\r
8223 nowChecked = IDM_EditGame;
\r
8225 case PlayFromGameFile:
\r
8226 nowChecked = IDM_LoadGame;
\r
8228 case EditPosition:
\r
8229 nowChecked = IDM_EditPosition;
\r
8232 nowChecked = IDM_Training;
\r
8234 case IcsPlayingWhite:
\r
8235 case IcsPlayingBlack:
\r
8236 case IcsObserving:
\r
8238 nowChecked = IDM_IcsClient;
\r
8245 if(prevChecked == IDM_TwoMachine) // [HGM] 'Machine Match' might have gotten disabled when stopping match
\r
8246 EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_ENABLED)
\r
8247 CheckMark(prevChecked, MF_UNCHECKED);
\r
8248 CheckMark(nowChecked, MF_CHECKED);
\r
8249 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8251 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8252 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8253 MF_BYCOMMAND|MF_ENABLED);
\r
8255 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8256 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8259 prevChecked = nowChecked;
\r
8261 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8262 if (appData.icsActive) {
\r
8263 if (appData.icsEngineAnalyze) {
\r
8264 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8266 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8269 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8275 HMENU hmenu = GetMenu(hwndMain);
\r
8276 SetMenuEnables(hmenu, icsEnables);
\r
8277 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8278 MF_BYCOMMAND|MF_ENABLED);
\r
8280 if (appData.zippyPlay) {
\r
8281 SetMenuEnables(hmenu, zippyEnables);
\r
8282 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8283 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8284 MF_BYCOMMAND|MF_ENABLED);
\r
8292 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8298 HMENU hmenu = GetMenu(hwndMain);
\r
8299 SetMenuEnables(hmenu, ncpEnables);
\r
8300 DrawMenuBar(hwndMain);
\r
8306 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8310 SetTrainingModeOn()
\r
8313 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8314 for (i = 0; i < N_BUTTONS; i++) {
\r
8315 if (buttonDesc[i].hwnd != NULL)
\r
8316 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8321 VOID SetTrainingModeOff()
\r
8324 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8325 for (i = 0; i < N_BUTTONS; i++) {
\r
8326 if (buttonDesc[i].hwnd != NULL)
\r
8327 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8333 SetUserThinkingEnables()
\r
8335 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8339 SetMachineThinkingEnables()
\r
8341 HMENU hMenu = GetMenu(hwndMain);
\r
8342 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8344 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8346 if (gameMode == MachinePlaysBlack) {
\r
8347 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8348 } else if (gameMode == MachinePlaysWhite) {
\r
8349 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8350 } else if (gameMode == TwoMachinesPlay) {
\r
8351 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8357 DisplayTitle(char *str)
\r
8359 char title[MSG_SIZ], *host;
\r
8360 if (str[0] != NULLCHAR) {
\r
8361 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8362 } else if (appData.icsActive) {
\r
8363 if (appData.icsCommPort[0] != NULLCHAR)
\r
8366 host = appData.icsHost;
\r
8367 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8368 } else if (appData.noChessProgram) {
\r
8369 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8371 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8372 strcat(title, ": ");
\r
8373 strcat(title, first.tidy);
\r
8375 SetWindowText(hwndMain, title);
\r
8380 DisplayMessage(char *str1, char *str2)
\r
8384 int remain = MESSAGE_TEXT_MAX - 1;
\r
8387 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8388 messageText[0] = NULLCHAR;
\r
8390 len = strlen(str1);
\r
8391 if (len > remain) len = remain;
\r
8392 strncpy(messageText, str1, len);
\r
8393 messageText[len] = NULLCHAR;
\r
8396 if (*str2 && remain >= 2) {
\r
8398 strcat(messageText, " ");
\r
8401 len = strlen(str2);
\r
8402 if (len > remain) len = remain;
\r
8403 strncat(messageText, str2, len);
\r
8405 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8406 safeStrCpy(lastMsg, messageText, MSG_SIZ);
\r
8408 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8412 hdc = GetDC(hwndMain);
\r
8413 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8414 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8415 &messageRect, messageText, strlen(messageText), NULL);
\r
8416 (void) SelectObject(hdc, oldFont);
\r
8417 (void) ReleaseDC(hwndMain, hdc);
\r
8421 DisplayError(char *str, int error)
\r
8423 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8427 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8429 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8430 NULL, error, LANG_NEUTRAL,
\r
8431 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8433 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8435 ErrorMap *em = errmap;
\r
8436 while (em->err != 0 && em->err != error) em++;
\r
8437 if (em->err != 0) {
\r
8438 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8440 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8445 ErrorPopUp(_("Error"), buf);
\r
8450 DisplayMoveError(char *str)
\r
8452 fromX = fromY = -1;
\r
8453 ClearHighlights();
\r
8454 DrawPosition(FALSE, NULL);
\r
8455 if (appData.popupMoveErrors) {
\r
8456 ErrorPopUp(_("Error"), str);
\r
8458 DisplayMessage(str, "");
\r
8459 moveErrorMessageUp = TRUE;
\r
8464 DisplayFatalError(char *str, int error, int exitStatus)
\r
8466 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8468 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8471 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8472 NULL, error, LANG_NEUTRAL,
\r
8473 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8475 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8477 ErrorMap *em = errmap;
\r
8478 while (em->err != 0 && em->err != error) em++;
\r
8479 if (em->err != 0) {
\r
8480 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8482 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8487 if (appData.debugMode) {
\r
8488 fprintf(debugFP, "%s: %s\n", label, str);
\r
8490 if (appData.popupExitMessage) {
\r
8491 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8492 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8494 ExitEvent(exitStatus);
\r
8499 DisplayInformation(char *str)
\r
8501 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8506 DisplayNote(char *str)
\r
8508 ErrorPopUp(_("Note"), str);
\r
8513 char *title, *question, *replyPrefix;
\r
8518 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8520 static QuestionParams *qp;
\r
8521 char reply[MSG_SIZ];
\r
8524 switch (message) {
\r
8525 case WM_INITDIALOG:
\r
8526 qp = (QuestionParams *) lParam;
\r
8527 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8528 Translate(hDlg, DLG_Question);
\r
8529 SetWindowText(hDlg, qp->title);
\r
8530 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8531 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8535 switch (LOWORD(wParam)) {
\r
8537 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8538 if (*reply) strcat(reply, " ");
\r
8539 len = strlen(reply);
\r
8540 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8541 strcat(reply, "\n");
\r
8542 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8543 EndDialog(hDlg, TRUE);
\r
8544 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8547 EndDialog(hDlg, FALSE);
\r
8558 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8560 QuestionParams qp;
\r
8564 qp.question = question;
\r
8565 qp.replyPrefix = replyPrefix;
\r
8567 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8568 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8569 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8570 FreeProcInstance(lpProc);
\r
8573 /* [AS] Pick FRC position */
\r
8574 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8576 static int * lpIndexFRC;
\r
8582 case WM_INITDIALOG:
\r
8583 lpIndexFRC = (int *) lParam;
\r
8585 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8586 Translate(hDlg, DLG_NewGameFRC);
\r
8588 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8589 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8590 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8591 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8596 switch( LOWORD(wParam) ) {
\r
8598 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8599 EndDialog( hDlg, 0 );
\r
8600 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8603 EndDialog( hDlg, 1 );
\r
8605 case IDC_NFG_Edit:
\r
8606 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8607 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8609 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8612 case IDC_NFG_Random:
\r
8613 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8614 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8627 int index = appData.defaultFrcPosition;
\r
8628 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8630 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8632 if( result == 0 ) {
\r
8633 appData.defaultFrcPosition = index;
\r
8639 /* [AS] Game list options. Refactored by HGM */
\r
8641 HWND gameListOptionsDialog;
\r
8643 // low-level front-end: clear text edit / list widget
\r
8648 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8651 // low-level front-end: clear text edit / list widget
\r
8653 GLT_DeSelectList()
\r
8655 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8658 // low-level front-end: append line to text edit / list widget
\r
8660 GLT_AddToList( char *name )
\r
8663 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8667 // low-level front-end: get line from text edit / list widget
\r
8669 GLT_GetFromList( int index, char *name )
\r
8672 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8678 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8680 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8681 int idx2 = idx1 + delta;
\r
8682 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8684 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8687 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8688 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8689 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8690 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8694 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8698 case WM_INITDIALOG:
\r
8699 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8701 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8702 Translate(hDlg, DLG_GameListOptions);
\r
8704 /* Initialize list */
\r
8705 GLT_TagsToList( lpUserGLT );
\r
8707 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8712 switch( LOWORD(wParam) ) {
\r
8715 EndDialog( hDlg, 0 );
\r
8718 EndDialog( hDlg, 1 );
\r
8721 case IDC_GLT_Default:
\r
8722 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8725 case IDC_GLT_Restore:
\r
8726 GLT_TagsToList( appData.gameListTags );
\r
8730 GLT_MoveSelection( hDlg, -1 );
\r
8733 case IDC_GLT_Down:
\r
8734 GLT_MoveSelection( hDlg, +1 );
\r
8744 int GameListOptions()
\r
8747 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8749 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8751 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8753 if( result == 0 ) {
\r
8754 char *oldTags = appData.gameListTags;
\r
8755 /* [AS] Memory leak here! */
\r
8756 appData.gameListTags = strdup( lpUserGLT );
\r
8757 if(strcmp(oldTags, appData.gameListTags)) // [HGM] redo Game List when we changed something
\r
8758 GameListToListBox(NULL, TRUE, ".", NULL, FALSE, FALSE); // "." as filter is kludge to select all
\r
8765 DisplayIcsInteractionTitle(char *str)
\r
8767 char consoleTitle[MSG_SIZ];
\r
8769 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8770 SetWindowText(hwndConsole, consoleTitle);
\r
8772 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
8773 char buf[MSG_SIZ], *p = buf, *q;
\r
8774 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
8776 q = strchr(p, ';');
\r
8778 if(*p) ChatPopUp(p);
\r
8782 SetActiveWindow(hwndMain);
\r
8786 DrawPosition(int fullRedraw, Board board)
\r
8788 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8791 void NotifyFrontendLogin()
\r
8794 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8800 fromX = fromY = -1;
\r
8801 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8802 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8803 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8804 dragInfo.lastpos = dragInfo.pos;
\r
8805 dragInfo.start.x = dragInfo.start.y = -1;
\r
8806 dragInfo.from = dragInfo.start;
\r
8808 DrawPosition(TRUE, NULL);
\r
8815 CommentPopUp(char *title, char *str)
\r
8817 HWND hwnd = GetActiveWindow();
\r
8818 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8820 SetActiveWindow(hwnd);
\r
8824 CommentPopDown(void)
\r
8826 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8827 if (commentDialog) {
\r
8828 ShowWindow(commentDialog, SW_HIDE);
\r
8830 commentUp = FALSE;
\r
8834 EditCommentPopUp(int index, char *title, char *str)
\r
8836 EitherCommentPopUp(index, title, str, TRUE);
\r
8843 MyPlaySound(&sounds[(int)SoundRoar]);
\r
8850 MyPlaySound(&sounds[(int)SoundMove]);
\r
8853 VOID PlayIcsWinSound()
\r
8855 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8858 VOID PlayIcsLossSound()
\r
8860 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8863 VOID PlayIcsDrawSound()
\r
8865 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8868 VOID PlayIcsUnfinishedSound()
\r
8870 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8876 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8882 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8890 consoleEcho = TRUE;
\r
8891 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8892 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8893 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8902 consoleEcho = FALSE;
\r
8903 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8904 /* This works OK: set text and background both to the same color */
\r
8906 cf.crTextColor = COLOR_ECHOOFF;
\r
8907 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8908 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8911 /* No Raw()...? */
\r
8913 void Colorize(ColorClass cc, int continuation)
\r
8915 currentColorClass = cc;
\r
8916 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8917 consoleCF.crTextColor = textAttribs[cc].color;
\r
8918 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8919 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8925 static char buf[MSG_SIZ];
\r
8926 DWORD bufsiz = MSG_SIZ;
\r
8928 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8929 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8931 if (!GetUserName(buf, &bufsiz)) {
\r
8932 /*DisplayError("Error getting user name", GetLastError());*/
\r
8933 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8941 static char buf[MSG_SIZ];
\r
8942 DWORD bufsiz = MSG_SIZ;
\r
8944 if (!GetComputerName(buf, &bufsiz)) {
\r
8945 /*DisplayError("Error getting host name", GetLastError());*/
\r
8946 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8953 ClockTimerRunning()
\r
8955 return clockTimerEvent != 0;
\r
8961 if (clockTimerEvent == 0) return FALSE;
\r
8962 KillTimer(hwndMain, clockTimerEvent);
\r
8963 clockTimerEvent = 0;
\r
8968 StartClockTimer(long millisec)
\r
8970 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8971 (UINT) millisec, NULL);
\r
8975 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8978 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8980 if(appData.noGUI) return;
\r
8981 hdc = GetDC(hwndMain);
\r
8982 if (!IsIconic(hwndMain)) {
\r
8983 DisplayAClock(hdc, timeRemaining, highlight,
\r
8984 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8986 if (highlight && iconCurrent == iconBlack) {
\r
8987 iconCurrent = iconWhite;
\r
8988 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8989 if (IsIconic(hwndMain)) {
\r
8990 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8993 (void) ReleaseDC(hwndMain, hdc);
\r
8995 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8999 DisplayBlackClock(long timeRemaining, int highlight)
\r
9002 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
9005 if(appData.noGUI) return;
\r
9006 hdc = GetDC(hwndMain);
\r
9007 if (!IsIconic(hwndMain)) {
\r
9008 DisplayAClock(hdc, timeRemaining, highlight,
\r
9009 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
9011 if (highlight && iconCurrent == iconWhite) {
\r
9012 iconCurrent = iconBlack;
\r
9013 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
9014 if (IsIconic(hwndMain)) {
\r
9015 DrawIcon(hdc, 2, 2, iconCurrent);
\r
9018 (void) ReleaseDC(hwndMain, hdc);
\r
9020 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
9025 LoadGameTimerRunning()
\r
9027 return loadGameTimerEvent != 0;
\r
9031 StopLoadGameTimer()
\r
9033 if (loadGameTimerEvent == 0) return FALSE;
\r
9034 KillTimer(hwndMain, loadGameTimerEvent);
\r
9035 loadGameTimerEvent = 0;
\r
9040 StartLoadGameTimer(long millisec)
\r
9042 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
9043 (UINT) millisec, NULL);
\r
9051 char fileTitle[MSG_SIZ];
\r
9053 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
9054 f = OpenFileDialog(hwndMain, "a", defName,
\r
9055 appData.oldSaveStyle ? "gam" : "pgn",
\r
9057 _("Save Game to File"), NULL, fileTitle, NULL);
\r
9059 SaveGame(f, 0, "");
\r
9066 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
9068 if (delayedTimerEvent != 0) {
\r
9069 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
9070 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
9072 KillTimer(hwndMain, delayedTimerEvent);
\r
9073 delayedTimerEvent = 0;
\r
9074 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
9075 delayedTimerCallback();
\r
9077 delayedTimerCallback = cb;
\r
9078 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
9079 (UINT) millisec, NULL);
\r
9082 DelayedEventCallback
\r
9085 if (delayedTimerEvent) {
\r
9086 return delayedTimerCallback;
\r
9093 CancelDelayedEvent()
\r
9095 if (delayedTimerEvent) {
\r
9096 KillTimer(hwndMain, delayedTimerEvent);
\r
9097 delayedTimerEvent = 0;
\r
9101 DWORD GetWin32Priority(int nice)
\r
9102 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
9104 REALTIME_PRIORITY_CLASS 0x00000100
\r
9105 HIGH_PRIORITY_CLASS 0x00000080
\r
9106 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
9107 NORMAL_PRIORITY_CLASS 0x00000020
\r
9108 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
9109 IDLE_PRIORITY_CLASS 0x00000040
\r
9111 if (nice < -15) return 0x00000080;
\r
9112 if (nice < 0) return 0x00008000;
\r
9113 if (nice == 0) return 0x00000020;
\r
9114 if (nice < 15) return 0x00004000;
\r
9115 return 0x00000040;
\r
9118 void RunCommand(char *cmdLine)
\r
9120 /* Now create the child process. */
\r
9121 STARTUPINFO siStartInfo;
\r
9122 PROCESS_INFORMATION piProcInfo;
\r
9124 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9125 siStartInfo.lpReserved = NULL;
\r
9126 siStartInfo.lpDesktop = NULL;
\r
9127 siStartInfo.lpTitle = NULL;
\r
9128 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9129 siStartInfo.cbReserved2 = 0;
\r
9130 siStartInfo.lpReserved2 = NULL;
\r
9131 siStartInfo.hStdInput = NULL;
\r
9132 siStartInfo.hStdOutput = NULL;
\r
9133 siStartInfo.hStdError = NULL;
\r
9135 CreateProcess(NULL,
\r
9136 cmdLine, /* command line */
\r
9137 NULL, /* process security attributes */
\r
9138 NULL, /* primary thread security attrs */
\r
9139 TRUE, /* handles are inherited */
\r
9140 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9141 NULL, /* use parent's environment */
\r
9143 &siStartInfo, /* STARTUPINFO pointer */
\r
9144 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9146 CloseHandle(piProcInfo.hThread);
\r
9149 /* Start a child process running the given program.
\r
9150 The process's standard output can be read from "from", and its
\r
9151 standard input can be written to "to".
\r
9152 Exit with fatal error if anything goes wrong.
\r
9153 Returns an opaque pointer that can be used to destroy the process
\r
9157 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
9159 #define BUFSIZE 4096
\r
9161 HANDLE hChildStdinRd, hChildStdinWr,
\r
9162 hChildStdoutRd, hChildStdoutWr;
\r
9163 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
9164 SECURITY_ATTRIBUTES saAttr;
\r
9166 PROCESS_INFORMATION piProcInfo;
\r
9167 STARTUPINFO siStartInfo;
\r
9169 char buf[MSG_SIZ];
\r
9172 if (appData.debugMode) {
\r
9173 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
9178 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
9179 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
9180 saAttr.bInheritHandle = TRUE;
\r
9181 saAttr.lpSecurityDescriptor = NULL;
\r
9184 * The steps for redirecting child's STDOUT:
\r
9185 * 1. Create anonymous pipe to be STDOUT for child.
\r
9186 * 2. Create a noninheritable duplicate of read handle,
\r
9187 * and close the inheritable read handle.
\r
9190 /* Create a pipe for the child's STDOUT. */
\r
9191 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
9192 return GetLastError();
\r
9195 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
9196 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
9197 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
9198 FALSE, /* not inherited */
\r
9199 DUPLICATE_SAME_ACCESS);
\r
9201 return GetLastError();
\r
9203 CloseHandle(hChildStdoutRd);
\r
9206 * The steps for redirecting child's STDIN:
\r
9207 * 1. Create anonymous pipe to be STDIN for child.
\r
9208 * 2. Create a noninheritable duplicate of write handle,
\r
9209 * and close the inheritable write handle.
\r
9212 /* Create a pipe for the child's STDIN. */
\r
9213 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
9214 return GetLastError();
\r
9217 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
9218 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
9219 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
9220 FALSE, /* not inherited */
\r
9221 DUPLICATE_SAME_ACCESS);
\r
9223 return GetLastError();
\r
9225 CloseHandle(hChildStdinWr);
\r
9227 /* Arrange to (1) look in dir for the child .exe file, and
\r
9228 * (2) have dir be the child's working directory. Interpret
\r
9229 * dir relative to the directory WinBoard loaded from. */
\r
9230 GetCurrentDirectory(MSG_SIZ, buf);
\r
9231 SetCurrentDirectory(installDir);
\r
9232 SetCurrentDirectory(dir);
\r
9234 /* Now create the child process. */
\r
9236 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9237 siStartInfo.lpReserved = NULL;
\r
9238 siStartInfo.lpDesktop = NULL;
\r
9239 siStartInfo.lpTitle = NULL;
\r
9240 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9241 siStartInfo.cbReserved2 = 0;
\r
9242 siStartInfo.lpReserved2 = NULL;
\r
9243 siStartInfo.hStdInput = hChildStdinRd;
\r
9244 siStartInfo.hStdOutput = hChildStdoutWr;
\r
9245 siStartInfo.hStdError = hChildStdoutWr;
\r
9247 fSuccess = CreateProcess(NULL,
\r
9248 cmdLine, /* command line */
\r
9249 NULL, /* process security attributes */
\r
9250 NULL, /* primary thread security attrs */
\r
9251 TRUE, /* handles are inherited */
\r
9252 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9253 NULL, /* use parent's environment */
\r
9255 &siStartInfo, /* STARTUPINFO pointer */
\r
9256 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9258 err = GetLastError();
\r
9259 SetCurrentDirectory(buf); /* return to prev directory */
\r
9264 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
9265 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
9266 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
9269 /* Close the handles we don't need in the parent */
\r
9270 CloseHandle(piProcInfo.hThread);
\r
9271 CloseHandle(hChildStdinRd);
\r
9272 CloseHandle(hChildStdoutWr);
\r
9274 /* Prepare return value */
\r
9275 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9276 cp->kind = CPReal;
\r
9277 cp->hProcess = piProcInfo.hProcess;
\r
9278 cp->pid = piProcInfo.dwProcessId;
\r
9279 cp->hFrom = hChildStdoutRdDup;
\r
9280 cp->hTo = hChildStdinWrDup;
\r
9282 *pr = (void *) cp;
\r
9284 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
9285 2000 where engines sometimes don't see the initial command(s)
\r
9286 from WinBoard and hang. I don't understand how that can happen,
\r
9287 but the Sleep is harmless, so I've put it in. Others have also
\r
9288 reported what may be the same problem, so hopefully this will fix
\r
9289 it for them too. */
\r
9297 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
9299 ChildProc *cp; int result;
\r
9301 cp = (ChildProc *) pr;
\r
9302 if (cp == NULL) return;
\r
9304 switch (cp->kind) {
\r
9306 /* TerminateProcess is considered harmful, so... */
\r
9307 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9308 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9309 /* The following doesn't work because the chess program
\r
9310 doesn't "have the same console" as WinBoard. Maybe
\r
9311 we could arrange for this even though neither WinBoard
\r
9312 nor the chess program uses a console for stdio? */
\r
9313 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9315 /* [AS] Special termination modes for misbehaving programs... */
\r
9316 if( signal & 8 ) {
\r
9317 result = TerminateProcess( cp->hProcess, 0 );
\r
9319 if ( appData.debugMode) {
\r
9320 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9323 else if( signal & 4 ) {
\r
9324 DWORD dw = WaitForSingleObject( cp->hProcess, appData.delayAfterQuit*1000 + 50 ); // Wait 3 seconds at most
\r
9326 if( dw != WAIT_OBJECT_0 ) {
\r
9327 result = TerminateProcess( cp->hProcess, 0 );
\r
9329 if ( appData.debugMode) {
\r
9330 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9336 CloseHandle(cp->hProcess);
\r
9340 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9344 closesocket(cp->sock);
\r
9349 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9350 closesocket(cp->sock);
\r
9351 closesocket(cp->sock2);
\r
9359 InterruptChildProcess(ProcRef pr)
\r
9363 cp = (ChildProc *) pr;
\r
9364 if (cp == NULL) return;
\r
9365 switch (cp->kind) {
\r
9367 /* The following doesn't work because the chess program
\r
9368 doesn't "have the same console" as WinBoard. Maybe
\r
9369 we could arrange for this even though neither WinBoard
\r
9370 nor the chess program uses a console for stdio */
\r
9371 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9376 /* Can't interrupt */
\r
9380 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9387 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9389 char cmdLine[MSG_SIZ];
\r
9391 if (port[0] == NULLCHAR) {
\r
9392 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9394 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9396 return StartChildProcess(cmdLine, "", pr);
\r
9400 /* Code to open TCP sockets */
\r
9403 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9409 struct sockaddr_in sa, mysa;
\r
9410 struct hostent FAR *hp;
\r
9411 unsigned short uport;
\r
9412 WORD wVersionRequested;
\r
9415 /* Initialize socket DLL */
\r
9416 wVersionRequested = MAKEWORD(1, 1);
\r
9417 err = WSAStartup(wVersionRequested, &wsaData);
\r
9418 if (err != 0) return err;
\r
9421 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9422 err = WSAGetLastError();
\r
9427 /* Bind local address using (mostly) don't-care values.
\r
9429 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9430 mysa.sin_family = AF_INET;
\r
9431 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9432 uport = (unsigned short) 0;
\r
9433 mysa.sin_port = htons(uport);
\r
9434 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9435 == SOCKET_ERROR) {
\r
9436 err = WSAGetLastError();
\r
9441 /* Resolve remote host name */
\r
9442 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9443 if (!(hp = gethostbyname(host))) {
\r
9444 unsigned int b0, b1, b2, b3;
\r
9446 err = WSAGetLastError();
\r
9448 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9449 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9450 hp->h_addrtype = AF_INET;
\r
9452 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9453 hp->h_addr_list[0] = (char *) malloc(4);
\r
9454 hp->h_addr_list[0][0] = (char) b0;
\r
9455 hp->h_addr_list[0][1] = (char) b1;
\r
9456 hp->h_addr_list[0][2] = (char) b2;
\r
9457 hp->h_addr_list[0][3] = (char) b3;
\r
9463 sa.sin_family = hp->h_addrtype;
\r
9464 uport = (unsigned short) atoi(port);
\r
9465 sa.sin_port = htons(uport);
\r
9466 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9468 /* Make connection */
\r
9469 if (connect(s, (struct sockaddr *) &sa,
\r
9470 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9471 err = WSAGetLastError();
\r
9476 /* Prepare return value */
\r
9477 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9478 cp->kind = CPSock;
\r
9480 *pr = (ProcRef *) cp;
\r
9486 OpenCommPort(char *name, ProcRef *pr)
\r
9491 char fullname[MSG_SIZ];
\r
9493 if (*name != '\\')
\r
9494 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9496 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9498 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9499 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9500 if (h == (HANDLE) -1) {
\r
9501 return GetLastError();
\r
9505 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9507 /* Accumulate characters until a 100ms pause, then parse */
\r
9508 ct.ReadIntervalTimeout = 100;
\r
9509 ct.ReadTotalTimeoutMultiplier = 0;
\r
9510 ct.ReadTotalTimeoutConstant = 0;
\r
9511 ct.WriteTotalTimeoutMultiplier = 0;
\r
9512 ct.WriteTotalTimeoutConstant = 0;
\r
9513 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9515 /* Prepare return value */
\r
9516 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9517 cp->kind = CPComm;
\r
9520 *pr = (ProcRef *) cp;
\r
9526 OpenLoopback(ProcRef *pr)
\r
9528 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9534 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9539 struct sockaddr_in sa, mysa;
\r
9540 struct hostent FAR *hp;
\r
9541 unsigned short uport;
\r
9542 WORD wVersionRequested;
\r
9545 char stderrPortStr[MSG_SIZ];
\r
9547 /* Initialize socket DLL */
\r
9548 wVersionRequested = MAKEWORD(1, 1);
\r
9549 err = WSAStartup(wVersionRequested, &wsaData);
\r
9550 if (err != 0) return err;
\r
9552 /* Resolve remote host name */
\r
9553 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9554 if (!(hp = gethostbyname(host))) {
\r
9555 unsigned int b0, b1, b2, b3;
\r
9557 err = WSAGetLastError();
\r
9559 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9560 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9561 hp->h_addrtype = AF_INET;
\r
9563 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9564 hp->h_addr_list[0] = (char *) malloc(4);
\r
9565 hp->h_addr_list[0][0] = (char) b0;
\r
9566 hp->h_addr_list[0][1] = (char) b1;
\r
9567 hp->h_addr_list[0][2] = (char) b2;
\r
9568 hp->h_addr_list[0][3] = (char) b3;
\r
9574 sa.sin_family = hp->h_addrtype;
\r
9575 uport = (unsigned short) 514;
\r
9576 sa.sin_port = htons(uport);
\r
9577 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9579 /* Bind local socket to unused "privileged" port address
\r
9581 s = INVALID_SOCKET;
\r
9582 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9583 mysa.sin_family = AF_INET;
\r
9584 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9585 for (fromPort = 1023;; fromPort--) {
\r
9586 if (fromPort < 0) {
\r
9588 return WSAEADDRINUSE;
\r
9590 if (s == INVALID_SOCKET) {
\r
9591 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9592 err = WSAGetLastError();
\r
9597 uport = (unsigned short) fromPort;
\r
9598 mysa.sin_port = htons(uport);
\r
9599 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9600 == SOCKET_ERROR) {
\r
9601 err = WSAGetLastError();
\r
9602 if (err == WSAEADDRINUSE) continue;
\r
9606 if (connect(s, (struct sockaddr *) &sa,
\r
9607 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9608 err = WSAGetLastError();
\r
9609 if (err == WSAEADDRINUSE) {
\r
9620 /* Bind stderr local socket to unused "privileged" port address
\r
9622 s2 = INVALID_SOCKET;
\r
9623 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9624 mysa.sin_family = AF_INET;
\r
9625 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9626 for (fromPort = 1023;; fromPort--) {
\r
9627 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9628 if (fromPort < 0) {
\r
9629 (void) closesocket(s);
\r
9631 return WSAEADDRINUSE;
\r
9633 if (s2 == INVALID_SOCKET) {
\r
9634 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9635 err = WSAGetLastError();
\r
9641 uport = (unsigned short) fromPort;
\r
9642 mysa.sin_port = htons(uport);
\r
9643 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9644 == SOCKET_ERROR) {
\r
9645 err = WSAGetLastError();
\r
9646 if (err == WSAEADDRINUSE) continue;
\r
9647 (void) closesocket(s);
\r
9651 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9652 err = WSAGetLastError();
\r
9653 if (err == WSAEADDRINUSE) {
\r
9655 s2 = INVALID_SOCKET;
\r
9658 (void) closesocket(s);
\r
9659 (void) closesocket(s2);
\r
9665 prevStderrPort = fromPort; // remember port used
\r
9666 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9668 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9669 err = WSAGetLastError();
\r
9670 (void) closesocket(s);
\r
9671 (void) closesocket(s2);
\r
9676 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9677 err = WSAGetLastError();
\r
9678 (void) closesocket(s);
\r
9679 (void) closesocket(s2);
\r
9683 if (*user == NULLCHAR) user = UserName();
\r
9684 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9685 err = WSAGetLastError();
\r
9686 (void) closesocket(s);
\r
9687 (void) closesocket(s2);
\r
9691 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9692 err = WSAGetLastError();
\r
9693 (void) closesocket(s);
\r
9694 (void) closesocket(s2);
\r
9699 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9700 err = WSAGetLastError();
\r
9701 (void) closesocket(s);
\r
9702 (void) closesocket(s2);
\r
9706 (void) closesocket(s2); /* Stop listening */
\r
9708 /* Prepare return value */
\r
9709 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9710 cp->kind = CPRcmd;
\r
9713 *pr = (ProcRef *) cp;
\r
9720 AddInputSource(ProcRef pr, int lineByLine,
\r
9721 InputCallback func, VOIDSTAR closure)
\r
9723 InputSource *is, *is2 = NULL;
\r
9724 ChildProc *cp = (ChildProc *) pr;
\r
9726 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9727 is->lineByLine = lineByLine;
\r
9729 is->closure = closure;
\r
9730 is->second = NULL;
\r
9731 is->next = is->buf;
\r
9732 if (pr == NoProc) {
\r
9733 is->kind = CPReal;
\r
9734 consoleInputSource = is;
\r
9736 is->kind = cp->kind;
\r
9738 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9739 we create all threads suspended so that the is->hThread variable can be
\r
9740 safely assigned, then let the threads start with ResumeThread.
\r
9742 switch (cp->kind) {
\r
9744 is->hFile = cp->hFrom;
\r
9745 cp->hFrom = NULL; /* now owned by InputThread */
\r
9747 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9748 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9752 is->hFile = cp->hFrom;
\r
9753 cp->hFrom = NULL; /* now owned by InputThread */
\r
9755 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9756 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9760 is->sock = cp->sock;
\r
9762 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9763 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9767 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9769 is->sock = cp->sock;
\r
9771 is2->sock = cp->sock2;
\r
9772 is2->second = is2;
\r
9774 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9775 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9777 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9778 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9782 if( is->hThread != NULL ) {
\r
9783 ResumeThread( is->hThread );
\r
9786 if( is2 != NULL && is2->hThread != NULL ) {
\r
9787 ResumeThread( is2->hThread );
\r
9791 return (InputSourceRef) is;
\r
9795 RemoveInputSource(InputSourceRef isr)
\r
9799 is = (InputSource *) isr;
\r
9800 is->hThread = NULL; /* tell thread to stop */
\r
9801 CloseHandle(is->hThread);
\r
9802 if (is->second != NULL) {
\r
9803 is->second->hThread = NULL;
\r
9804 CloseHandle(is->second->hThread);
\r
9808 int no_wrap(char *message, int count)
\r
9810 ConsoleOutput(message, count, FALSE);
\r
9815 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9818 int outCount = SOCKET_ERROR;
\r
9819 ChildProc *cp = (ChildProc *) pr;
\r
9820 static OVERLAPPED ovl;
\r
9822 static int line = 0;
\r
9826 if (appData.noJoin || !appData.useInternalWrap)
\r
9827 return no_wrap(message, count);
\r
9830 int width = get_term_width();
\r
9831 int len = wrap(NULL, message, count, width, &line);
\r
9832 char *msg = malloc(len);
\r
9836 return no_wrap(message, count);
\r
9839 dbgchk = wrap(msg, message, count, width, &line);
\r
9840 if (dbgchk != len && appData.debugMode)
\r
9841 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9842 ConsoleOutput(msg, len, FALSE);
\r
9849 if (ovl.hEvent == NULL) {
\r
9850 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9852 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9854 switch (cp->kind) {
\r
9857 outCount = send(cp->sock, message, count, 0);
\r
9858 if (outCount == SOCKET_ERROR) {
\r
9859 *outError = WSAGetLastError();
\r
9861 *outError = NO_ERROR;
\r
9866 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9867 &dOutCount, NULL)) {
\r
9868 *outError = NO_ERROR;
\r
9869 outCount = (int) dOutCount;
\r
9871 *outError = GetLastError();
\r
9876 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9877 &dOutCount, &ovl);
\r
9878 if (*outError == NO_ERROR) {
\r
9879 outCount = (int) dOutCount;
\r
9889 if(n != 0) Sleep(n);
\r
9893 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9896 /* Ignore delay, not implemented for WinBoard */
\r
9897 return OutputToProcess(pr, message, count, outError);
\r
9902 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9903 char *buf, int count, int error)
\r
9905 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9908 /* see wgamelist.c for Game List functions */
\r
9909 /* see wedittags.c for Edit Tags functions */
\r
9916 char buf[MSG_SIZ];
\r
9919 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9920 f = fopen(buf, "r");
\r
9922 ProcessICSInitScript(f);
\r
9932 StartAnalysisClock()
\r
9934 if (analysisTimerEvent) return;
\r
9935 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9936 (UINT) 2000, NULL);
\r
9940 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9942 highlightInfo.sq[0].x = fromX;
\r
9943 highlightInfo.sq[0].y = fromY;
\r
9944 highlightInfo.sq[1].x = toX;
\r
9945 highlightInfo.sq[1].y = toY;
\r
9951 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9952 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9956 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9958 premoveHighlightInfo.sq[0].x = fromX;
\r
9959 premoveHighlightInfo.sq[0].y = fromY;
\r
9960 premoveHighlightInfo.sq[1].x = toX;
\r
9961 premoveHighlightInfo.sq[1].y = toY;
\r
9965 ClearPremoveHighlights()
\r
9967 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9968 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9972 ShutDownFrontEnd()
\r
9974 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9975 DeleteClipboardTempFiles();
\r
9981 if (IsIconic(hwndMain))
\r
9982 ShowWindow(hwndMain, SW_RESTORE);
\r
9984 SetActiveWindow(hwndMain);
\r
9988 * Prototypes for animation support routines
\r
9990 static void ScreenSquare(int column, int row, POINT * pt);
\r
9991 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9992 POINT frames[], int * nFrames);
\r
9998 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9999 { // [HGM] atomic: animate blast wave
\r
10002 explodeInfo.fromX = fromX;
\r
10003 explodeInfo.fromY = fromY;
\r
10004 explodeInfo.toX = toX;
\r
10005 explodeInfo.toY = toY;
\r
10006 for(i=1; i<4*kFactor; i++) {
\r
10007 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
10008 DrawPosition(FALSE, board);
\r
10009 Sleep(appData.animSpeed);
\r
10011 explodeInfo.radius = 0;
\r
10012 DrawPosition(TRUE, board);
\r
10016 AnimateMove(board, fromX, fromY, toX, toY)
\r
10023 ChessSquare piece;
\r
10024 int x = toX, y = toY;
\r
10025 POINT start, finish, mid;
\r
10026 POINT frames[kFactor * 2 + 1];
\r
10029 if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();
\r
10031 if (!appData.animate) return;
\r
10032 if (doingSizing) return;
\r
10033 if (fromY < 0 || fromX < 0) return;
\r
10034 piece = board[fromY][fromX];
\r
10035 if (piece >= EmptySquare) return;
\r
10037 if(killX >= 0) toX = killX, toY = killY; // [HGM] lion: first to kill square
\r
10041 ScreenSquare(fromX, fromY, &start);
\r
10042 ScreenSquare(toX, toY, &finish);
\r
10044 /* All moves except knight jumps move in straight line */
\r
10045 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
10046 mid.x = start.x + (finish.x - start.x) / 2;
\r
10047 mid.y = start.y + (finish.y - start.y) / 2;
\r
10049 /* Knight: make straight movement then diagonal */
\r
10050 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
10051 mid.x = start.x + (finish.x - start.x) / 2;
\r
10055 mid.y = start.y + (finish.y - start.y) / 2;
\r
10059 /* Don't use as many frames for very short moves */
\r
10060 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
10061 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
10063 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
10065 animInfo.from.x = fromX;
\r
10066 animInfo.from.y = fromY;
\r
10067 animInfo.to.x = toX;
\r
10068 animInfo.to.y = toY;
\r
10069 animInfo.lastpos = start;
\r
10070 animInfo.piece = piece;
\r
10071 for (n = 0; n < nFrames; n++) {
\r
10072 animInfo.pos = frames[n];
\r
10073 DrawPosition(FALSE, NULL);
\r
10074 animInfo.lastpos = animInfo.pos;
\r
10075 Sleep(appData.animSpeed);
\r
10077 animInfo.pos = finish;
\r
10078 DrawPosition(FALSE, NULL);
\r
10080 if(toX != x || toY != y) { fromX = toX; fromY = toY; toX = x; toY = y; goto again; } // second leg
\r
10082 animInfo.piece = EmptySquare;
\r
10083 Explode(board, fromX, fromY, toX, toY);
\r
10086 /* Convert board position to corner of screen rect and color */
\r
10089 ScreenSquare(column, row, pt)
\r
10090 int column; int row; POINT * pt;
\r
10093 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
10094 pt->y = lineGap + row * (squareSize + lineGap) + border;
\r
10096 pt->x = lineGap + column * (squareSize + lineGap) + border;
\r
10097 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
10101 /* Generate a series of frame coords from start->mid->finish.
\r
10102 The movement rate doubles until the half way point is
\r
10103 reached, then halves back down to the final destination,
\r
10104 which gives a nice slow in/out effect. The algorithmn
\r
10105 may seem to generate too many intermediates for short
\r
10106 moves, but remember that the purpose is to attract the
\r
10107 viewers attention to the piece about to be moved and
\r
10108 then to where it ends up. Too few frames would be less
\r
10112 Tween(start, mid, finish, factor, frames, nFrames)
\r
10113 POINT * start; POINT * mid;
\r
10114 POINT * finish; int factor;
\r
10115 POINT frames[]; int * nFrames;
\r
10117 int n, fraction = 1, count = 0;
\r
10119 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
10120 for (n = 0; n < factor; n++)
\r
10122 for (n = 0; n < factor; n++) {
\r
10123 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
10124 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
10126 fraction = fraction / 2;
\r
10130 frames[count] = *mid;
\r
10133 /* Slow out, stepping 1/2, then 1/4, ... */
\r
10135 for (n = 0; n < factor; n++) {
\r
10136 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
10137 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
10139 fraction = fraction * 2;
\r
10141 *nFrames = count;
\r
10145 SettingsPopUp(ChessProgramState *cps)
\r
10146 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
10147 EngineOptionsPopup(savedHwnd, cps);
\r
10150 int flock(int fid, int code)
\r
10152 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
10154 ov.hEvent = NULL;
\r
10156 ov.OffsetHigh = 0;
\r
10158 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
10160 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
10161 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
10162 default: return -1;
\r
10171 static char col[8][20];
\r
10172 COLORREF color = *(COLORREF *) colorVariable[n];
\r
10174 snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
10179 ActivateTheme (int new)
\r
10180 { // Redo initialization of features depending on options that can occur in themes
\r
10182 if(new) InitDrawingColors();
\r
10183 fontBitmapSquareSize = 0; // request creation of new font pieces
\r
10184 InitDrawingSizes(boardSize, 0);
\r
10185 InvalidateRect(hwndMain, NULL, TRUE);
\r