2 * WinBoard.c -- Windows NT front end to XBoard
\r
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
\r
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
\r
8 * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc.
\r
10 * Enhancements Copyright 2005 Alessandro Scotti
\r
12 * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,
\r
13 * which was written and is copyrighted by Wayne Christopher.
\r
15 * The following terms apply to Digital Equipment Corporation's copyright
\r
16 * interest in XBoard:
\r
17 * ------------------------------------------------------------------------
\r
18 * All Rights Reserved
\r
20 * Permission to use, copy, modify, and distribute this software and its
\r
21 * documentation for any purpose and without fee is hereby granted,
\r
22 * provided that the above copyright notice appear in all copies and that
\r
23 * both that copyright notice and this permission notice appear in
\r
24 * supporting documentation, and that the name of Digital not be
\r
25 * used in advertising or publicity pertaining to distribution of the
\r
26 * software without specific, written prior permission.
\r
28 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
\r
29 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
\r
30 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
\r
31 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
\r
32 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
\r
33 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
\r
35 * ------------------------------------------------------------------------
\r
37 * The following terms apply to the enhanced version of XBoard
\r
38 * distributed by the Free Software Foundation:
\r
39 * ------------------------------------------------------------------------
\r
41 * GNU XBoard is free software: you can redistribute it and/or modify
\r
42 * it under the terms of the GNU General Public License as published by
\r
43 * the Free Software Foundation, either version 3 of the License, or (at
\r
44 * your option) any later version.
\r
46 * GNU XBoard is distributed in the hope that it will be useful, but
\r
47 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
48 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
49 * General Public License for more details.
\r
51 * You should have received a copy of the GNU General Public License
\r
52 * along with this program. If not, see http://www.gnu.org/licenses/. *
\r
54 *------------------------------------------------------------------------
\r
55 ** See the file ChangeLog for a revision history. */
\r
59 #include <windows.h>
\r
60 #include <winuser.h>
\r
61 #include <winsock.h>
\r
62 #include <commctrl.h>
\r
68 #include <sys/stat.h>
\r
71 #include <commdlg.h>
\r
73 #include <richedit.h>
\r
74 #include <mmsystem.h>
\r
84 #include "frontend.h"
\r
85 #include "backend.h"
\r
86 #include "winboard.h"
\r
88 #include "wclipbrd.h"
\r
89 #include "woptions.h"
\r
90 #include "wsockerr.h"
\r
91 #include "defaults.h"
\r
96 #define DATADIR "~~"
\r
98 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
\r
100 int myrandom(void);
\r
101 void mysrandom(unsigned int seed);
\r
103 extern int whiteFlag, blackFlag;
\r
104 Boolean flipClock = FALSE;
\r
105 extern HANDLE chatHandle[];
\r
106 extern enum ICS_TYPE ics_type;
\r
108 int MySearchPath P((char *installDir, char *name, char *fullname));
\r
109 int MyGetFullPathName P((char *name, char *fullname));
\r
110 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
111 VOID NewVariantPopup(HWND hwnd);
\r
112 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
113 /*char*/int promoChar));
\r
114 void DisplayMove P((int moveNumber));
\r
115 void ChatPopUp P((char *s));
\r
117 ChessSquare piece;
\r
118 POINT pos; /* window coordinates of current pos */
\r
119 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
120 POINT from; /* board coordinates of the piece's orig pos */
\r
121 POINT to; /* board coordinates of the piece's new pos */
\r
124 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
127 POINT start; /* window coordinates of start pos */
\r
128 POINT pos; /* window coordinates of current pos */
\r
129 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
130 POINT from; /* board coordinates of the piece's orig pos */
\r
134 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };
\r
137 POINT sq[2]; /* board coordinates of from, to squares */
\r
140 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
141 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
142 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
143 static HighlightInfo oldPartnerHighlight = { {{-1, -1}, {-1, -1}} };
\r
145 typedef struct { // [HGM] atomic
\r
146 int fromX, fromY, toX, toY, radius;
\r
149 static ExplodeInfo explodeInfo;
\r
151 /* Window class names */
\r
152 char szAppName[] = "WinBoard";
\r
153 char szConsoleName[] = "WBConsole";
\r
155 /* Title bar text */
\r
156 char szTitle[] = "WinBoard";
\r
157 char szConsoleTitle[] = "I C S Interaction";
\r
160 char *settingsFileName;
\r
161 Boolean saveSettingsOnExit;
\r
162 char installDir[MSG_SIZ];
\r
163 int errorExitStatus;
\r
165 BoardSize boardSize;
\r
166 Boolean chessProgram;
\r
167 //static int boardX, boardY;
\r
168 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
169 int squareSize, lineGap, minorSize, border;
\r
170 static int winW, winH;
\r
171 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
172 static int logoHeight = 0;
\r
173 static char messageText[MESSAGE_TEXT_MAX];
\r
174 static int clockTimerEvent = 0;
\r
175 static int loadGameTimerEvent = 0;
\r
176 static int analysisTimerEvent = 0;
\r
177 static DelayedEventCallback delayedTimerCallback;
\r
178 static int delayedTimerEvent = 0;
\r
179 static int buttonCount = 2;
\r
180 char *icsTextMenuString;
\r
182 char *firstChessProgramNames;
\r
183 char *secondChessProgramNames;
\r
185 #define PALETTESIZE 256
\r
187 HINSTANCE hInst; /* current instance */
\r
188 Boolean alwaysOnTop = FALSE;
\r
190 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
191 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
192 COLORREF markerColor[8] = { 0x00FFFF, 0x0000FF, 0x00FF00, 0xFF0000, 0xFFFF00, 0xFF00FF, 0xFFFFFF, 0x000000 };
\r
194 ColorClass currentColorClass;
\r
196 static HWND savedHwnd;
\r
197 HWND hCommPort = NULL; /* currently open comm port */
\r
198 static HWND hwndPause; /* pause button */
\r
199 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
200 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
201 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
202 explodeBrush, /* [HGM] atomic */
\r
203 markerBrush[8], /* [HGM] markers */
\r
204 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
205 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
206 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
207 static HPEN gridPen = NULL;
\r
208 static HPEN highlightPen = NULL;
\r
209 static HPEN premovePen = NULL;
\r
210 static NPLOGPALETTE pLogPal;
\r
211 static BOOL paletteChanged = FALSE;
\r
212 static HICON iconWhite, iconBlack, iconCurrent;
\r
213 static int doingSizing = FALSE;
\r
214 static int lastSizing = 0;
\r
215 static int prevStderrPort;
\r
216 static HBITMAP userLogo;
\r
218 static HBITMAP liteBackTexture = NULL;
\r
219 static HBITMAP darkBackTexture = NULL;
\r
220 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
221 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
222 static int backTextureSquareSize = 0;
\r
223 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
225 #if __GNUC__ && !defined(_winmajor)
\r
226 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
229 #if defined(_winmajor)
\r
230 #define oldDialog (_winmajor < 4)
\r
232 #define oldDialog 0
\r
236 #define INTERNATIONAL
\r
238 #ifdef INTERNATIONAL
\r
239 # define _(s) T_(s)
\r
245 # define Translate(x, y)
\r
246 # define LoadLanguageFile(s)
\r
249 #ifdef INTERNATIONAL
\r
251 Boolean barbaric; // flag indicating if translation is needed
\r
253 // list of item numbers used in each dialog (used to alter language at run time)
\r
255 #define ABOUTBOX -1 /* not sure why these are needed */
\r
256 #define ABOUTBOX2 -1
\r
258 int dialogItems[][42] = {
\r
259 { ABOUTBOX, IDOK, OPT_MESS, 400 },
\r
260 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed,
\r
261 OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors, IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL },
\r
262 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,
\r
263 OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds,
\r
264 OPT_Ranget, IDOK, IDCANCEL },
\r
265 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
266 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
267 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
268 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
269 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
270 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
271 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
272 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
273 { ABOUTBOX2, IDC_ChessBoard },
\r
274 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
275 OPT_GameListClose, IDC_GameListDoFilter },
\r
276 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
277 { DLG_Error, IDOK },
\r
278 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
279 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
280 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
281 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
282 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
283 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
284 { DLG_IndexNumber, IDC_Index },
\r
285 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
286 { DLG_TypeInName, IDOK, IDCANCEL },
\r
287 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
288 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
289 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
290 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
291 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
292 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
293 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
294 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
295 OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove },
\r
296 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
297 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
298 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
299 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
300 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
301 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
302 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
303 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
304 GPB_General, GPB_Alarm, OPT_AutoCreate },
\r
305 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
306 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
307 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
308 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
309 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
310 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
311 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
312 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid },
\r
313 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
314 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
315 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
316 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
317 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
318 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
319 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,
\r
320 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
321 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
322 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
323 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
324 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,
\r
325 OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7,
\r
326 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
327 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
328 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
329 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
330 { DLG_MoveHistory },
\r
331 { DLG_EvalGraph },
\r
332 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
333 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
334 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
335 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
336 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
337 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
338 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
339 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
340 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
344 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];
\r
345 static int lastChecked;
\r
346 static char oldLanguage[MSG_SIZ], *menuText[10][30];
\r
347 extern int tinyLayout;
\r
348 extern char * menuBarText[][10];
\r
351 LoadLanguageFile(char *name)
\r
352 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
354 int i=0, j=0, n=0, k;
\r
357 if(!name || name[0] == NULLCHAR) return;
\r
358 snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension
\r
359 appData.language = oldLanguage;
\r
360 if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on
\r
361 if((f = fopen(buf, "r")) == NULL) return;
\r
362 while((k = fgetc(f)) != EOF) {
\r
363 if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }
\r
364 languageBuf[i] = k;
\r
366 if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {
\r
368 if(p = strstr(languageBuf + n + 1, "\" === \"")) {
\r
369 if(p > languageBuf+n+2 && p+8 < languageBuf+i) {
\r
370 if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }
\r
371 english[j] = languageBuf + n + 1; *p = 0;
\r
372 foreign[j++] = p + 7; languageBuf[i-1] = 0;
\r
373 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);
\r
378 } else if(i > 0 && languageBuf[i-1] == '\\') {
\r
380 case 'n': k = '\n'; break;
\r
381 case 'r': k = '\r'; break;
\r
382 case 't': k = '\t'; break;
\r
384 languageBuf[--i] = k;
\r
389 barbaric = (j != 0);
\r
390 safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );
\r
395 { // return the translation of the given string
\r
396 // efficiency can be improved a lot...
\r
398 static char buf[MSG_SIZ];
\r
399 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
400 if(!barbaric) return s;
\r
401 if(!s) return ""; // sanity
\r
402 while(english[i]) {
\r
403 if(!strcmp(s, english[i])) return foreign[i];
\r
404 if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending
\r
405 snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion
\r
414 Translate(HWND hDlg, int dialogID)
\r
415 { // translate all text items in the given dialog
\r
417 char buf[MSG_SIZ], *s;
\r
418 if(!barbaric) return;
\r
419 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
420 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
421 GetWindowText( hDlg, buf, MSG_SIZ );
\r
423 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
424 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
425 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
426 if(strlen(buf) == 0) continue;
\r
428 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
433 TranslateOneMenu(int i, HMENU subMenu)
\r
436 static MENUITEMINFO info;
\r
438 info.cbSize = sizeof(MENUITEMINFO);
\r
439 info.fMask = MIIM_STATE | MIIM_TYPE;
\r
440 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
442 info.dwTypeData = buf;
\r
443 info.cch = sizeof(buf);
\r
444 GetMenuItemInfo(subMenu, j, TRUE, &info);
\r
446 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );
\r
447 else menuText[i][j] = strdup(buf); // remember original on first change
\r
449 if(buf[0] == NULLCHAR) continue;
\r
450 info.dwTypeData = T_(buf);
\r
451 info.cch = strlen(buf)+1;
\r
452 SetMenuItemInfo(subMenu, j, TRUE, &info);
\r
458 TranslateMenus(int addLanguage)
\r
461 WIN32_FIND_DATA fileData;
\r
463 #define IDM_English 1970
\r
465 HMENU mainMenu = GetMenu(hwndMain);
\r
466 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
467 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
468 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
469 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
470 TranslateOneMenu(i, subMenu);
\r
472 DrawMenuBar(hwndMain);
\r
475 if(!addLanguage) return;
\r
476 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
477 HMENU mainMenu = GetMenu(hwndMain);
\r
478 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
479 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
480 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
481 i = 0; lastChecked = IDM_English;
\r
483 char *p, *q = fileData.cFileName;
\r
484 int checkFlag = MF_UNCHECKED;
\r
485 languageFile[i] = strdup(q);
\r
486 if(barbaric && !strcmp(oldLanguage, q)) {
\r
487 checkFlag = MF_CHECKED;
\r
488 lastChecked = IDM_English + i + 1;
\r
489 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
491 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
492 p = strstr(fileData.cFileName, ".lng");
\r
494 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
495 } while(FindNextFile(hFind, &fileData));
\r
502 #define IDM_RecentEngines 3000
\r
505 RecentEngineMenu (char *s)
\r
507 if(appData.icsActive) return;
\r
508 if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty
\r
509 HMENU mainMenu = GetMenu(hwndMain);
\r
510 HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu
\r
511 int i=IDM_RecentEngines;
\r
512 recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu
\r
513 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
515 char *p = strchr(s, '\n');
\r
516 if(p == NULL) return; // malformed!
\r
518 AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);
\r
532 int cliWidth, cliHeight;
\r
535 SizeInfo sizeInfo[] =
\r
537 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
538 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
539 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
540 { "petite", 33, 1, 1, 1, 0, 0 },
\r
541 { "slim", 37, 2, 1, 0, 0, 0 },
\r
542 { "small", 40, 2, 1, 0, 0, 0 },
\r
543 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
544 { "middling", 49, 2, 0, 0, 0, 0 },
\r
545 { "average", 54, 2, 0, 0, 0, 0 },
\r
546 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
547 { "medium", 64, 3, 0, 0, 0, 0 },
\r
548 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
549 { "large", 80, 3, 0, 0, 0, 0 },
\r
550 { "big", 87, 3, 0, 0, 0, 0 },
\r
551 { "huge", 95, 3, 0, 0, 0, 0 },
\r
552 { "giant", 108, 3, 0, 0, 0, 0 },
\r
553 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
554 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
555 { NULL, 0, 0, 0, 0, 0, 0 }
\r
558 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
559 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
561 { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
562 { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
563 { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
564 { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
565 { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
566 { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
567 { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
568 { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
569 { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
570 { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
571 { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
572 { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
573 { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
574 { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
575 { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
576 { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
577 { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL), MF (GAMELIST_FONT_ALL) },
\r
578 { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
581 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
590 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
591 #define N_BUTTONS 5
\r
593 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
595 {"<<", IDM_ToStart, NULL, NULL},
\r
596 {"<", IDM_Backward, NULL, NULL},
\r
597 {"P", IDM_Pause, NULL, NULL},
\r
598 {">", IDM_Forward, NULL, NULL},
\r
599 {">>", IDM_ToEnd, NULL, NULL},
\r
602 int tinyLayout = 0, smallLayout = 0;
\r
603 #define MENU_BAR_ITEMS 9
\r
604 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
605 { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },
\r
606 { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },
\r
610 MySound sounds[(int)NSoundClasses];
\r
611 MyTextAttribs textAttribs[(int)NColorClasses];
\r
613 MyColorizeAttribs colorizeAttribs[] = {
\r
614 { (COLORREF)0, 0, N_("Shout Text") },
\r
615 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
616 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
617 { (COLORREF)0, 0, N_("Channel Text") },
\r
618 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
619 { (COLORREF)0, 0, N_("Tell Text") },
\r
620 { (COLORREF)0, 0, N_("Challenge Text") },
\r
621 { (COLORREF)0, 0, N_("Request Text") },
\r
622 { (COLORREF)0, 0, N_("Seek Text") },
\r
623 { (COLORREF)0, 0, N_("Normal Text") },
\r
624 { (COLORREF)0, 0, N_("None") }
\r
629 static char *commentTitle;
\r
630 static char *commentText;
\r
631 static int commentIndex;
\r
632 static Boolean editComment = FALSE;
\r
635 char errorTitle[MSG_SIZ];
\r
636 char errorMessage[2*MSG_SIZ];
\r
637 HWND errorDialog = NULL;
\r
638 BOOLEAN moveErrorMessageUp = FALSE;
\r
639 BOOLEAN consoleEcho = TRUE;
\r
640 CHARFORMAT consoleCF;
\r
641 COLORREF consoleBackgroundColor;
\r
643 char *programVersion;
\r
649 typedef int CPKind;
\r
658 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
661 #define INPUT_SOURCE_BUF_SIZE 4096
\r
663 typedef struct _InputSource {
\r
670 char buf[INPUT_SOURCE_BUF_SIZE];
\r
674 InputCallback func;
\r
675 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
679 InputSource *consoleInputSource;
\r
684 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
685 VOID ConsoleCreate();
\r
687 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
688 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
689 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
690 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
692 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
693 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
694 void ParseIcsTextMenu(char *icsTextMenuString);
\r
695 VOID PopUpNameDialog(char firstchar);
\r
696 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
700 int GameListOptions();
\r
702 int dummy; // [HGM] for obsolete args
\r
704 HWND hwndMain = NULL; /* root window*/
\r
705 HWND hwndConsole = NULL;
\r
706 HWND commentDialog = NULL;
\r
707 HWND moveHistoryDialog = NULL;
\r
708 HWND evalGraphDialog = NULL;
\r
709 HWND engineOutputDialog = NULL;
\r
710 HWND gameListDialog = NULL;
\r
711 HWND editTagsDialog = NULL;
\r
713 int commentUp = FALSE;
\r
715 WindowPlacement wpMain;
\r
716 WindowPlacement wpConsole;
\r
717 WindowPlacement wpComment;
\r
718 WindowPlacement wpMoveHistory;
\r
719 WindowPlacement wpEvalGraph;
\r
720 WindowPlacement wpEngineOutput;
\r
721 WindowPlacement wpGameList;
\r
722 WindowPlacement wpTags;
\r
724 VOID EngineOptionsPopup(); // [HGM] settings
\r
726 VOID GothicPopUp(char *title, VariantClass variant);
\r
728 * Setting "frozen" should disable all user input other than deleting
\r
729 * the window. We do this while engines are initializing themselves.
\r
731 static int frozen = 0;
\r
732 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
738 if (frozen) return;
\r
740 hmenu = GetMenu(hwndMain);
\r
741 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
742 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
744 DrawMenuBar(hwndMain);
\r
747 /* Undo a FreezeUI */
\r
753 if (!frozen) return;
\r
755 hmenu = GetMenu(hwndMain);
\r
756 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
757 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
759 DrawMenuBar(hwndMain);
\r
762 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
764 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
770 #define JAWS_ALT_INTERCEPT
\r
771 #define JAWS_KBUP_NAVIGATION
\r
772 #define JAWS_KBDOWN_NAVIGATION
\r
773 #define JAWS_MENU_ITEMS
\r
774 #define JAWS_SILENCE
\r
775 #define JAWS_REPLAY
\r
777 #define JAWS_COPYRIGHT
\r
778 #define JAWS_DELETE(X) X
\r
779 #define SAYMACHINEMOVE()
\r
783 /*---------------------------------------------------------------------------*\
\r
787 \*---------------------------------------------------------------------------*/
\r
789 static void HandleMessage P((MSG *message));
\r
790 static HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
793 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
794 LPSTR lpCmdLine, int nCmdShow)
\r
797 // INITCOMMONCONTROLSEX ex;
\r
801 LoadLibrary("RICHED32.DLL");
\r
802 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
804 if (!InitApplication(hInstance)) {
\r
807 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
814 // InitCommonControlsEx(&ex);
\r
815 InitCommonControls();
\r
817 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
818 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
819 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
821 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
823 while (GetMessage(&msg, /* message structure */
\r
824 NULL, /* handle of window receiving the message */
\r
825 0, /* lowest message to examine */
\r
826 0)) /* highest message to examine */
\r
828 HandleMessage(&msg);
\r
832 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
836 HandleMessage (MSG *message)
\r
838 MSG msg = *message;
\r
840 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
841 // [HGM] navigate: switch between all windows with tab
\r
842 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
843 int i, currentElement = 0;
\r
845 // first determine what element of the chain we come from (if any)
\r
846 if(appData.icsActive) {
\r
847 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
848 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
850 if(engineOutputDialog && EngineOutputIsUp()) {
\r
851 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
852 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
854 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
855 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
857 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
858 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
859 if(msg.hwnd == e1) currentElement = 2; else
\r
860 if(msg.hwnd == e2) currentElement = 3; else
\r
861 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
862 if(msg.hwnd == mh) currentElement = 4; else
\r
863 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
864 if(msg.hwnd == hText) currentElement = 5; else
\r
865 if(msg.hwnd == hInput) currentElement = 6; else
\r
866 for (i = 0; i < N_BUTTONS; i++) {
\r
867 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
870 // determine where to go to
\r
871 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
873 currentElement = (currentElement + direction) % 7;
\r
874 switch(currentElement) {
\r
876 h = hwndMain; break; // passing this case always makes the loop exit
\r
878 h = buttonDesc[0].hwnd; break; // could be NULL
\r
880 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
883 if(!EngineOutputIsUp()) continue;
\r
886 if(!MoveHistoryIsUp()) continue;
\r
888 // case 6: // input to eval graph does not seem to get here!
\r
889 // if(!EvalGraphIsUp()) continue;
\r
890 // h = evalGraphDialog; break;
\r
892 if(!appData.icsActive) continue;
\r
896 if(!appData.icsActive) continue;
\r
902 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
903 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
906 return; // this message now has been processed
\r
910 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
911 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
912 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
913 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
914 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
915 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
916 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
917 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
918 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
919 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
920 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
921 for(i=0; i<MAX_CHAT; i++)
\r
922 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
925 if(done) return; // [HGM] chat: end patch
\r
926 TranslateMessage(&msg); /* Translates virtual key codes */
\r
927 DispatchMessage(&msg); /* Dispatches message to window */
\r
933 { /* Dispatch pending messages */
\r
935 while (PeekMessage(&msg, /* message structure */
\r
936 NULL, /* handle of window receiving the message */
\r
937 0, /* lowest message to examine */
\r
938 0, /* highest message to examine */
\r
941 HandleMessage(&msg);
\r
945 /*---------------------------------------------------------------------------*\
\r
947 * Initialization functions
\r
949 \*---------------------------------------------------------------------------*/
\r
953 { // update user logo if necessary
\r
954 static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;
\r
956 if(appData.autoLogo) {
\r
957 curName = UserName();
\r
958 if(strcmp(curName, oldUserName)) {
\r
959 GetCurrentDirectory(MSG_SIZ, dir);
\r
960 SetCurrentDirectory(installDir);
\r
961 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
962 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
963 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
964 if(userLogo == NULL)
\r
965 userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
966 SetCurrentDirectory(dir); /* return to prev directory */
\r
972 InitApplication(HINSTANCE hInstance)
\r
976 /* Fill in window class structure with parameters that describe the */
\r
979 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
980 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
981 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
982 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
983 wc.hInstance = hInstance; /* Owner of this class */
\r
984 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
985 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
986 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
987 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
988 wc.lpszClassName = szAppName; /* Name to register as */
\r
990 /* Register the window class and return success/failure code. */
\r
991 if (!RegisterClass(&wc)) return FALSE;
\r
993 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
994 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
996 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
997 wc.hInstance = hInstance;
\r
998 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
999 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
1000 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
1001 wc.lpszMenuName = NULL;
\r
1002 wc.lpszClassName = szConsoleName;
\r
1004 if (!RegisterClass(&wc)) return FALSE;
\r
1009 /* Set by InitInstance, used by EnsureOnScreen */
\r
1010 int screenHeight, screenWidth;
\r
1011 RECT screenGeometry;
\r
1014 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
1016 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
1017 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
1018 if (*x > screenGeometry.right - 32) *x = screenGeometry.left;
\r
1019 if (*y > screenGeometry.bottom - 32) *y = screenGeometry.top;
\r
1020 if (*x < screenGeometry.left + minX) *x = screenGeometry.left + minX;
\r
1021 if (*y < screenGeometry.top + minY) *y = screenGeometry.top + minY;
\r
1025 LoadLogo(ChessProgramState *cps, int n, Boolean ics)
\r
1027 char buf[MSG_SIZ], dir[MSG_SIZ];
\r
1028 GetCurrentDirectory(MSG_SIZ, dir);
\r
1029 SetCurrentDirectory(installDir);
\r
1030 if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {
\r
1031 cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1033 if (cps->programLogo == NULL && appData.debugMode) {
\r
1034 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );
\r
1036 } else if(appData.autoLogo) {
\r
1037 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
\r
1038 char *opponent = "";
\r
1039 if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;
\r
1040 if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;
\r
1041 sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);
\r
1042 if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {
\r
1043 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
1044 cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1047 if(appData.directory[n] && appData.directory[n][0]) {
\r
1048 SetCurrentDirectory(appData.directory[n]);
\r
1049 cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1052 SetCurrentDirectory(dir); /* return to prev directory */
\r
1058 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
1059 backTextureSquareSize = 0; // kludge to force recalculation of texturemode
\r
1061 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1062 if(liteBackTexture) DeleteObject(liteBackTexture);
\r
1063 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1064 liteBackTextureMode = appData.liteBackTextureMode;
\r
1066 if (liteBackTexture == NULL && appData.debugMode) {
\r
1067 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1071 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1072 if(darkBackTexture) DeleteObject(darkBackTexture);
\r
1073 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1074 darkBackTextureMode = appData.darkBackTextureMode;
\r
1076 if (darkBackTexture == NULL && appData.debugMode) {
\r
1077 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1082 #ifndef SM_CXVIRTUALSCREEN
\r
1083 #define SM_CXVIRTUALSCREEN 78
\r
1085 #ifndef SM_CYVIRTUALSCREEN
\r
1086 #define SM_CYVIRTUALSCREEN 79
\r
1088 #ifndef SM_XVIRTUALSCREEN
\r
1089 #define SM_XVIRTUALSCREEN 76
\r
1091 #ifndef SM_YVIRTUALSCREEN
\r
1092 #define SM_YVIRTUALSCREEN 77
\r
1098 screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
\r
1099 if( !screenHeight ) screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1100 screenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
\r
1101 if( !screenWidth ) screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1102 screenGeometry.left = GetSystemMetrics(SM_XVIRTUALSCREEN);
\r
1103 screenGeometry.top = GetSystemMetrics(SM_YVIRTUALSCREEN);
\r
1104 screenGeometry.right = screenGeometry.left + screenWidth;
\r
1105 screenGeometry.bottom = screenGeometry.top + screenHeight;
\r
1109 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
1111 HWND hwnd; /* Main window handle. */
\r
1113 WINDOWPLACEMENT wp;
\r
1116 hInst = hInstance; /* Store instance handle in our global variable */
\r
1117 programName = szAppName;
\r
1119 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
1120 *filepart = NULLCHAR;
\r
1121 SetCurrentDirectory(installDir);
\r
1123 GetCurrentDirectory(MSG_SIZ, installDir);
\r
1125 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
1127 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
1128 /* xboard, and older WinBoards, controlled the move sound with the
\r
1129 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
1130 always turn the option on (so that the backend will call us),
\r
1131 then let the user turn the sound off by setting it to silence if
\r
1132 desired. To accommodate old winboard.ini files saved by old
\r
1133 versions of WinBoard, we also turn off the sound if the option
\r
1134 was initially set to false. [HGM] taken out of InitAppData */
\r
1135 if (!appData.ringBellAfterMoves) {
\r
1136 sounds[(int)SoundMove].name = strdup("");
\r
1137 appData.ringBellAfterMoves = TRUE;
\r
1139 if (appData.debugMode) {
\r
1140 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
1141 setbuf(debugFP, NULL);
\r
1144 LoadLanguageFile(appData.language);
\r
1148 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
1149 // InitEngineUCI( installDir, &second );
\r
1151 /* Create a main window for this application instance. */
\r
1152 hwnd = CreateWindow(szAppName, szTitle,
\r
1153 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
1154 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
1155 NULL, NULL, hInstance, NULL);
\r
1158 /* If window could not be created, return "failure" */
\r
1163 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1164 LoadLogo(&first, 0, FALSE);
\r
1165 LoadLogo(&second, 1, appData.icsActive);
\r
1169 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1170 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1171 iconCurrent = iconWhite;
\r
1172 InitDrawingColors();
\r
1174 InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args
\r
1175 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1176 /* Compute window size for each board size, and use the largest
\r
1177 size that fits on this screen as the default. */
\r
1178 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1179 if (boardSize == (BoardSize)-1 &&
\r
1180 winH <= screenHeight
\r
1181 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1182 && winW <= screenWidth) {
\r
1183 boardSize = (BoardSize)ibs;
\r
1187 InitDrawingSizes(boardSize, 0);
\r
1188 RecentEngineMenu(appData.recentEngineList);
\r
1190 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1192 /* [AS] Load textures if specified */
\r
1195 mysrandom( (unsigned) time(NULL) );
\r
1197 /* [AS] Restore layout */
\r
1198 if( wpMoveHistory.visible ) {
\r
1199 MoveHistoryPopUp();
\r
1202 if( wpEvalGraph.visible ) {
\r
1206 if( wpEngineOutput.visible ) {
\r
1207 EngineOutputPopUp();
\r
1210 /* Make the window visible; update its client area; and return "success" */
\r
1211 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1212 wp.length = sizeof(WINDOWPLACEMENT);
\r
1214 wp.showCmd = nCmdShow;
\r
1215 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1216 wp.rcNormalPosition.left = wpMain.x;
\r
1217 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1218 wp.rcNormalPosition.top = wpMain.y;
\r
1219 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1220 SetWindowPlacement(hwndMain, &wp);
\r
1222 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1224 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1225 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1227 if (hwndConsole) {
\r
1229 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1230 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1232 ShowWindow(hwndConsole, nCmdShow);
\r
1233 SetActiveWindow(hwndConsole);
\r
1235 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1236 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1245 HMENU hmenu = GetMenu(hwndMain);
\r
1247 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1248 MF_BYCOMMAND|((appData.icsActive &&
\r
1249 *appData.icsCommPort != NULLCHAR) ?
\r
1250 MF_ENABLED : MF_GRAYED));
\r
1251 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1252 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1253 MF_CHECKED : MF_UNCHECKED));
\r
1254 EnableMenuItem(hmenu, IDM_SaveSelected, MF_GRAYED);
\r
1257 //---------------------------------------------------------------------------------------------------------
\r
1259 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1260 #define XBOARD FALSE
\r
1262 #define OPTCHAR "/"
\r
1263 #define SEPCHAR "="
\r
1264 #define TOPLEVEL 0
\r
1268 // front-end part of option handling
\r
1271 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1273 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1274 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1277 lf->lfEscapement = 0;
\r
1278 lf->lfOrientation = 0;
\r
1279 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1280 lf->lfItalic = mfp->italic;
\r
1281 lf->lfUnderline = mfp->underline;
\r
1282 lf->lfStrikeOut = mfp->strikeout;
\r
1283 lf->lfCharSet = mfp->charset;
\r
1284 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1285 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1286 lf->lfQuality = DEFAULT_QUALITY;
\r
1287 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1288 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1292 CreateFontInMF(MyFont *mf)
\r
1294 LFfromMFP(&mf->lf, &mf->mfp);
\r
1295 if (mf->hf) DeleteObject(mf->hf);
\r
1296 mf->hf = CreateFontIndirect(&mf->lf);
\r
1299 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1301 colorVariable[] = {
\r
1302 &whitePieceColor,
\r
1303 &blackPieceColor,
\r
1304 &lightSquareColor,
\r
1305 &darkSquareColor,
\r
1306 &highlightSquareColor,
\r
1307 &premoveHighlightColor,
\r
1309 &consoleBackgroundColor,
\r
1310 &appData.fontForeColorWhite,
\r
1311 &appData.fontBackColorWhite,
\r
1312 &appData.fontForeColorBlack,
\r
1313 &appData.fontBackColorBlack,
\r
1314 &appData.evalHistColorWhite,
\r
1315 &appData.evalHistColorBlack,
\r
1316 &appData.highlightArrowColor,
\r
1319 /* Command line font name parser. NULL name means do nothing.
\r
1320 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1321 For backward compatibility, syntax without the colon is also
\r
1322 accepted, but font names with digits in them won't work in that case.
\r
1325 ParseFontName(char *name, MyFontParams *mfp)
\r
1328 if (name == NULL) return;
\r
1330 q = strchr(p, ':');
\r
1332 if (q - p >= sizeof(mfp->faceName))
\r
1333 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1334 memcpy(mfp->faceName, p, q - p);
\r
1335 mfp->faceName[q - p] = NULLCHAR;
\r
1338 q = mfp->faceName;
\r
1340 while (*p && !isdigit(*p)) {
\r
1342 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1343 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1345 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1348 if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);
\r
1349 mfp->pointSize = (float) atof(p);
\r
1350 mfp->bold = (strchr(p, 'b') != NULL);
\r
1351 mfp->italic = (strchr(p, 'i') != NULL);
\r
1352 mfp->underline = (strchr(p, 'u') != NULL);
\r
1353 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1354 mfp->charset = DEFAULT_CHARSET;
\r
1355 q = strchr(p, 'c');
\r
1357 mfp->charset = (BYTE) atoi(q+1);
\r
1361 ParseFont(char *name, int number)
\r
1362 { // wrapper to shield back-end from 'font'
\r
1363 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1368 { // in WB we have a 2D array of fonts; this initializes their description
\r
1370 /* Point font array elements to structures and
\r
1371 parse default font names */
\r
1372 for (i=0; i<NUM_FONTS; i++) {
\r
1373 for (j=0; j<NUM_SIZES; j++) {
\r
1374 font[j][i] = &fontRec[j][i];
\r
1375 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1382 { // here we create the actual fonts from the selected descriptions
\r
1384 for (i=0; i<NUM_FONTS; i++) {
\r
1385 for (j=0; j<NUM_SIZES; j++) {
\r
1386 CreateFontInMF(font[j][i]);
\r
1390 /* Color name parser.
\r
1391 X version accepts X color names, but this one
\r
1392 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1394 ParseColorName(char *name)
\r
1396 int red, green, blue, count;
\r
1397 char buf[MSG_SIZ];
\r
1399 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1401 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1402 &red, &green, &blue);
\r
1405 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1406 DisplayError(buf, 0);
\r
1407 return RGB(0, 0, 0);
\r
1409 return PALETTERGB(red, green, blue);
\r
1413 ParseColor(int n, char *name)
\r
1414 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1415 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1419 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1421 char *e = argValue;
\r
1425 if (*e == 'b') eff |= CFE_BOLD;
\r
1426 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1427 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1428 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1429 else if (*e == '#' || isdigit(*e)) break;
\r
1433 *color = ParseColorName(e);
\r
1437 ParseTextAttribs(ColorClass cc, char *s)
\r
1438 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1439 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1440 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1444 ParseBoardSize(void *addr, char *name)
\r
1445 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1446 BoardSize bs = SizeTiny;
\r
1447 while (sizeInfo[bs].name != NULL) {
\r
1448 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1449 *(BoardSize *)addr = bs;
\r
1454 ExitArgError(_("Unrecognized board size value"), name, TRUE);
\r
1459 { // [HGM] import name from appData first
\r
1462 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1463 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1464 textAttribs[cc].sound.data = NULL;
\r
1465 MyLoadSound(&textAttribs[cc].sound);
\r
1467 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1468 textAttribs[cc].sound.name = strdup("");
\r
1469 textAttribs[cc].sound.data = NULL;
\r
1471 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1472 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1473 sounds[sc].data = NULL;
\r
1474 MyLoadSound(&sounds[sc]);
\r
1479 SetCommPortDefaults()
\r
1481 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1482 dcb.DCBlength = sizeof(DCB);
\r
1483 dcb.BaudRate = 9600;
\r
1484 dcb.fBinary = TRUE;
\r
1485 dcb.fParity = FALSE;
\r
1486 dcb.fOutxCtsFlow = FALSE;
\r
1487 dcb.fOutxDsrFlow = FALSE;
\r
1488 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1489 dcb.fDsrSensitivity = FALSE;
\r
1490 dcb.fTXContinueOnXoff = TRUE;
\r
1491 dcb.fOutX = FALSE;
\r
1493 dcb.fNull = FALSE;
\r
1494 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1495 dcb.fAbortOnError = FALSE;
\r
1497 dcb.Parity = SPACEPARITY;
\r
1498 dcb.StopBits = ONESTOPBIT;
\r
1501 // [HGM] args: these three cases taken out to stay in front-end
\r
1503 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1504 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1505 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1506 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1508 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1509 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1510 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1511 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1512 ad->argName, mfp->faceName, mfp->pointSize,
\r
1513 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1514 mfp->bold ? "b" : "",
\r
1515 mfp->italic ? "i" : "",
\r
1516 mfp->underline ? "u" : "",
\r
1517 mfp->strikeout ? "s" : "",
\r
1518 (int)mfp->charset);
\r
1524 { // [HGM] copy the names from the internal WB variables to appData
\r
1527 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1528 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1529 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1530 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1534 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1535 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1536 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1537 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1538 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1539 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1540 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1541 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1542 (ta->effects) ? " " : "",
\r
1543 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1547 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1548 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1549 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1550 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1551 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1555 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1556 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1557 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1561 ParseCommPortSettings(char *s)
\r
1562 { // wrapper to keep dcb from back-end
\r
1563 ParseCommSettings(s, &dcb);
\r
1568 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1569 GetActualPlacement(hwndMain, &wpMain);
\r
1570 GetActualPlacement(hwndConsole, &wpConsole);
\r
1571 GetActualPlacement(commentDialog, &wpComment);
\r
1572 GetActualPlacement(editTagsDialog, &wpTags);
\r
1573 GetActualPlacement(gameListDialog, &wpGameList);
\r
1574 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1575 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1576 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1580 PrintCommPortSettings(FILE *f, char *name)
\r
1581 { // wrapper to shield back-end from DCB
\r
1582 PrintCommSettings(f, name, &dcb);
\r
1586 MySearchPath(char *installDir, char *name, char *fullname)
\r
1588 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1589 if(name[0]== '%') {
\r
1590 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1591 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1592 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1593 *strchr(buf, '%') = 0;
\r
1594 strcat(fullname, getenv(buf));
\r
1595 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1597 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1598 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1599 return (int) strlen(fullname);
\r
1601 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1605 MyGetFullPathName(char *name, char *fullname)
\r
1608 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1613 { // [HGM] args: allows testing if main window is realized from back-end
\r
1614 return hwndMain != NULL;
\r
1618 PopUpStartupDialog()
\r
1622 LoadLanguageFile(appData.language);
\r
1623 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1624 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1625 FreeProcInstance(lpProc);
\r
1628 /*---------------------------------------------------------------------------*\
\r
1630 * GDI board drawing routines
\r
1632 \*---------------------------------------------------------------------------*/
\r
1634 /* [AS] Draw square using background texture */
\r
1635 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1640 return; /* Should never happen! */
\r
1643 SetGraphicsMode( dst, GM_ADVANCED );
\r
1650 /* X reflection */
\r
1655 x.eDx = (FLOAT) dw + dx - 1;
\r
1658 SetWorldTransform( dst, &x );
\r
1661 /* Y reflection */
\r
1667 x.eDy = (FLOAT) dh + dy - 1;
\r
1669 SetWorldTransform( dst, &x );
\r
1677 x.eDx = (FLOAT) dx;
\r
1678 x.eDy = (FLOAT) dy;
\r
1681 SetWorldTransform( dst, &x );
\r
1685 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1693 SetWorldTransform( dst, &x );
\r
1695 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1698 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1700 PM_WP = (int) WhitePawn,
\r
1701 PM_WN = (int) WhiteKnight,
\r
1702 PM_WB = (int) WhiteBishop,
\r
1703 PM_WR = (int) WhiteRook,
\r
1704 PM_WQ = (int) WhiteQueen,
\r
1705 PM_WF = (int) WhiteFerz,
\r
1706 PM_WW = (int) WhiteWazir,
\r
1707 PM_WE = (int) WhiteAlfil,
\r
1708 PM_WM = (int) WhiteMan,
\r
1709 PM_WO = (int) WhiteCannon,
\r
1710 PM_WU = (int) WhiteUnicorn,
\r
1711 PM_WH = (int) WhiteNightrider,
\r
1712 PM_WA = (int) WhiteAngel,
\r
1713 PM_WC = (int) WhiteMarshall,
\r
1714 PM_WAB = (int) WhiteCardinal,
\r
1715 PM_WD = (int) WhiteDragon,
\r
1716 PM_WL = (int) WhiteLance,
\r
1717 PM_WS = (int) WhiteCobra,
\r
1718 PM_WV = (int) WhiteFalcon,
\r
1719 PM_WSG = (int) WhiteSilver,
\r
1720 PM_WG = (int) WhiteGrasshopper,
\r
1721 PM_WK = (int) WhiteKing,
\r
1722 PM_BP = (int) BlackPawn,
\r
1723 PM_BN = (int) BlackKnight,
\r
1724 PM_BB = (int) BlackBishop,
\r
1725 PM_BR = (int) BlackRook,
\r
1726 PM_BQ = (int) BlackQueen,
\r
1727 PM_BF = (int) BlackFerz,
\r
1728 PM_BW = (int) BlackWazir,
\r
1729 PM_BE = (int) BlackAlfil,
\r
1730 PM_BM = (int) BlackMan,
\r
1731 PM_BO = (int) BlackCannon,
\r
1732 PM_BU = (int) BlackUnicorn,
\r
1733 PM_BH = (int) BlackNightrider,
\r
1734 PM_BA = (int) BlackAngel,
\r
1735 PM_BC = (int) BlackMarshall,
\r
1736 PM_BG = (int) BlackGrasshopper,
\r
1737 PM_BAB = (int) BlackCardinal,
\r
1738 PM_BD = (int) BlackDragon,
\r
1739 PM_BL = (int) BlackLance,
\r
1740 PM_BS = (int) BlackCobra,
\r
1741 PM_BV = (int) BlackFalcon,
\r
1742 PM_BSG = (int) BlackSilver,
\r
1743 PM_BK = (int) BlackKing
\r
1746 static HFONT hPieceFont = NULL;
\r
1747 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1748 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1749 static int fontBitmapSquareSize = 0;
\r
1750 static char pieceToFontChar[(int) EmptySquare] =
\r
1751 { 'p', 'n', 'b', 'r', 'q',
\r
1752 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1753 'k', 'o', 'm', 'v', 't', 'w',
\r
1754 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1757 extern BOOL SetCharTable( char *table, const char * map );
\r
1758 /* [HGM] moved to backend.c */
\r
1760 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1763 BYTE r1 = GetRValue( color );
\r
1764 BYTE g1 = GetGValue( color );
\r
1765 BYTE b1 = GetBValue( color );
\r
1771 /* Create a uniform background first */
\r
1772 hbrush = CreateSolidBrush( color );
\r
1773 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1774 FillRect( hdc, &rc, hbrush );
\r
1775 DeleteObject( hbrush );
\r
1778 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1779 int steps = squareSize / 2;
\r
1782 for( i=0; i<steps; i++ ) {
\r
1783 BYTE r = r1 - (r1-r2) * i / steps;
\r
1784 BYTE g = g1 - (g1-g2) * i / steps;
\r
1785 BYTE b = b1 - (b1-b2) * i / steps;
\r
1787 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1788 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1789 FillRect( hdc, &rc, hbrush );
\r
1790 DeleteObject(hbrush);
\r
1793 else if( mode == 2 ) {
\r
1794 /* Diagonal gradient, good more or less for every piece */
\r
1795 POINT triangle[3];
\r
1796 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1797 HBRUSH hbrush_old;
\r
1798 int steps = squareSize;
\r
1801 triangle[0].x = squareSize - steps;
\r
1802 triangle[0].y = squareSize;
\r
1803 triangle[1].x = squareSize;
\r
1804 triangle[1].y = squareSize;
\r
1805 triangle[2].x = squareSize;
\r
1806 triangle[2].y = squareSize - steps;
\r
1808 for( i=0; i<steps; i++ ) {
\r
1809 BYTE r = r1 - (r1-r2) * i / steps;
\r
1810 BYTE g = g1 - (g1-g2) * i / steps;
\r
1811 BYTE b = b1 - (b1-b2) * i / steps;
\r
1813 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1814 hbrush_old = SelectObject( hdc, hbrush );
\r
1815 Polygon( hdc, triangle, 3 );
\r
1816 SelectObject( hdc, hbrush_old );
\r
1817 DeleteObject(hbrush);
\r
1822 SelectObject( hdc, hpen );
\r
1827 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1828 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1829 piece: follow the steps as explained below.
\r
1831 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1835 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1841 int backColor = whitePieceColor;
\r
1842 int foreColor = blackPieceColor;
\r
1844 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1845 backColor = appData.fontBackColorWhite;
\r
1846 foreColor = appData.fontForeColorWhite;
\r
1848 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1849 backColor = appData.fontBackColorBlack;
\r
1850 foreColor = appData.fontForeColorBlack;
\r
1854 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1856 hbm_old = SelectObject( hdc, hbm );
\r
1860 rc.right = squareSize;
\r
1861 rc.bottom = squareSize;
\r
1863 /* Step 1: background is now black */
\r
1864 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1866 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1868 pt.x = (squareSize - sz.cx) / 2;
\r
1869 pt.y = (squareSize - sz.cy) / 2;
\r
1871 SetBkMode( hdc, TRANSPARENT );
\r
1872 SetTextColor( hdc, chroma );
\r
1873 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1874 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1876 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1877 /* Step 3: the area outside the piece is filled with white */
\r
1878 // FloodFill( hdc, 0, 0, chroma );
\r
1879 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1880 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1881 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1882 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1883 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1885 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1886 but if the start point is not inside the piece we're lost!
\r
1887 There should be a better way to do this... if we could create a region or path
\r
1888 from the fill operation we would be fine for example.
\r
1890 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1891 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1893 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1894 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1895 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1897 SelectObject( dc2, bm2 );
\r
1898 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1899 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1900 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1901 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1902 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1905 DeleteObject( bm2 );
\r
1908 SetTextColor( hdc, 0 );
\r
1910 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1911 draw the piece again in black for safety.
\r
1913 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1915 SelectObject( hdc, hbm_old );
\r
1917 if( hPieceMask[index] != NULL ) {
\r
1918 DeleteObject( hPieceMask[index] );
\r
1921 hPieceMask[index] = hbm;
\r
1924 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1926 SelectObject( hdc, hbm );
\r
1929 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1930 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1931 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1933 SelectObject( dc1, hPieceMask[index] );
\r
1934 SelectObject( dc2, bm2 );
\r
1935 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1936 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1939 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1940 the piece background and deletes (makes transparent) the rest.
\r
1941 Thanks to that mask, we are free to paint the background with the greates
\r
1942 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1943 We use this, to make gradients and give the pieces a "roundish" look.
\r
1945 SetPieceBackground( hdc, backColor, 2 );
\r
1946 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1950 DeleteObject( bm2 );
\r
1953 SetTextColor( hdc, foreColor );
\r
1954 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1956 SelectObject( hdc, hbm_old );
\r
1958 if( hPieceFace[index] != NULL ) {
\r
1959 DeleteObject( hPieceFace[index] );
\r
1962 hPieceFace[index] = hbm;
\r
1965 static int TranslatePieceToFontPiece( int piece )
\r
1995 case BlackMarshall:
\r
1999 case BlackNightrider:
\r
2005 case BlackUnicorn:
\r
2009 case BlackGrasshopper:
\r
2021 case BlackCardinal:
\r
2028 case WhiteMarshall:
\r
2032 case WhiteNightrider:
\r
2038 case WhiteUnicorn:
\r
2042 case WhiteGrasshopper:
\r
2054 case WhiteCardinal:
\r
2063 void CreatePiecesFromFont()
\r
2066 HDC hdc_window = NULL;
\r
2072 if( fontBitmapSquareSize < 0 ) {
\r
2073 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
2077 if( !appData.useFont || appData.renderPiecesWithFont == NULL ||
\r
2078 appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
2079 fontBitmapSquareSize = -1;
\r
2083 if( fontBitmapSquareSize != squareSize ) {
\r
2084 hdc_window = GetDC( hwndMain );
\r
2085 hdc = CreateCompatibleDC( hdc_window );
\r
2087 if( hPieceFont != NULL ) {
\r
2088 DeleteObject( hPieceFont );
\r
2091 for( i=0; i<=(int)BlackKing; i++ ) {
\r
2092 hPieceMask[i] = NULL;
\r
2093 hPieceFace[i] = NULL;
\r
2099 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
2100 fontHeight = appData.fontPieceSize;
\r
2103 fontHeight = (fontHeight * squareSize) / 100;
\r
2105 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2107 lf.lfEscapement = 0;
\r
2108 lf.lfOrientation = 0;
\r
2109 lf.lfWeight = FW_NORMAL;
\r
2111 lf.lfUnderline = 0;
\r
2112 lf.lfStrikeOut = 0;
\r
2113 lf.lfCharSet = DEFAULT_CHARSET;
\r
2114 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2115 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2116 lf.lfQuality = PROOF_QUALITY;
\r
2117 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2118 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2119 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2121 hPieceFont = CreateFontIndirect( &lf );
\r
2123 if( hPieceFont == NULL ) {
\r
2124 fontBitmapSquareSize = -2;
\r
2127 /* Setup font-to-piece character table */
\r
2128 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2129 /* No (or wrong) global settings, try to detect the font */
\r
2130 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2132 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2134 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2135 /* DiagramTT* family */
\r
2136 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2138 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2139 /* Fairy symbols */
\r
2140 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2142 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2143 /* Good Companion (Some characters get warped as literal :-( */
\r
2144 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2145 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2146 SetCharTable(pieceToFontChar, s);
\r
2149 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2150 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2154 /* Create bitmaps */
\r
2155 hfont_old = SelectObject( hdc, hPieceFont );
\r
2156 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2157 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2158 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2160 SelectObject( hdc, hfont_old );
\r
2162 fontBitmapSquareSize = squareSize;
\r
2166 if( hdc != NULL ) {
\r
2170 if( hdc_window != NULL ) {
\r
2171 ReleaseDC( hwndMain, hdc_window );
\r
2176 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2178 char name[128], buf[MSG_SIZ];
\r
2180 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2181 if(appData.pieceDirectory[0]) {
\r
2183 snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);
\r
2184 res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
2185 if(res) return res;
\r
2187 if (gameInfo.event &&
\r
2188 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2189 strcmp(name, "k80s") == 0) {
\r
2190 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2192 return LoadBitmap(hinst, name);
\r
2196 /* Insert a color into the program's logical palette
\r
2197 structure. This code assumes the given color is
\r
2198 the result of the RGB or PALETTERGB macro, and it
\r
2199 knows how those macros work (which is documented).
\r
2202 InsertInPalette(COLORREF color)
\r
2204 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2206 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2207 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2208 pLogPal->palNumEntries--;
\r
2212 pe->peFlags = (char) 0;
\r
2213 pe->peRed = (char) (0xFF & color);
\r
2214 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2215 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2221 InitDrawingColors()
\r
2224 if (pLogPal == NULL) {
\r
2225 /* Allocate enough memory for a logical palette with
\r
2226 * PALETTESIZE entries and set the size and version fields
\r
2227 * of the logical palette structure.
\r
2229 pLogPal = (NPLOGPALETTE)
\r
2230 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2231 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2232 pLogPal->palVersion = 0x300;
\r
2234 pLogPal->palNumEntries = 0;
\r
2236 InsertInPalette(lightSquareColor);
\r
2237 InsertInPalette(darkSquareColor);
\r
2238 InsertInPalette(whitePieceColor);
\r
2239 InsertInPalette(blackPieceColor);
\r
2240 InsertInPalette(highlightSquareColor);
\r
2241 InsertInPalette(premoveHighlightColor);
\r
2243 /* create a logical color palette according the information
\r
2244 * in the LOGPALETTE structure.
\r
2246 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2248 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2249 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2250 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2251 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2252 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2253 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2254 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2255 for(i=0; i<8;i++) markerBrush[i] = CreateSolidBrush(markerColor[i]); // [HGM] markers
\r
2257 /* [AS] Force rendering of the font-based pieces */
\r
2258 if( fontBitmapSquareSize > 0 ) {
\r
2259 fontBitmapSquareSize = 0;
\r
2265 BoardWidth(int boardSize, int n)
\r
2266 { /* [HGM] argument n added to allow different width and height */
\r
2267 int lineGap = sizeInfo[boardSize].lineGap;
\r
2269 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2270 lineGap = appData.overrideLineGap;
\r
2273 return (n + 1) * lineGap +
\r
2274 n * sizeInfo[boardSize].squareSize;
\r
2277 /* Respond to board resize by dragging edge */
\r
2279 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2281 BoardSize newSize = NUM_SIZES - 1;
\r
2282 static int recurse = 0;
\r
2283 if (IsIconic(hwndMain)) return;
\r
2284 if (recurse > 0) return;
\r
2286 while (newSize > 0) {
\r
2287 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2288 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2289 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2292 boardSize = newSize;
\r
2293 InitDrawingSizes(boardSize, flags);
\r
2298 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2301 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2303 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2304 ChessSquare piece;
\r
2305 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2307 SIZE clockSize, messageSize;
\r
2309 char buf[MSG_SIZ];
\r
2311 HMENU hmenu = GetMenu(hwndMain);
\r
2312 RECT crect, wrect, oldRect;
\r
2314 LOGBRUSH logbrush;
\r
2315 VariantClass v = gameInfo.variant;
\r
2317 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2318 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2320 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2321 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2322 if(boardSize == -1) return; // no size defined yet; abort (to allow early call of InitPosition)
\r
2323 oldBoardSize = boardSize;
\r
2325 if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)
\r
2326 { // correct board size to one where built-in pieces exist
\r
2327 if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)
\r
2328 && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range
\r
2330 || (v == VariantShogi && boardSize != SizeModerate) // Japanese-style Shogi
\r
2331 || v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan
\r
2332 || v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy || v == VariantLion ) {
\r
2333 if(boardSize < SizeMediocre) boardSize = SizePetite; else
\r
2334 if(boardSize > SizeModerate) boardSize = SizeBulky; else
\r
2335 boardSize = SizeMiddling;
\r
2338 if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite
\r
2340 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2341 oldRect.top = wpMain.y;
\r
2342 oldRect.right = wpMain.x + wpMain.width;
\r
2343 oldRect.bottom = wpMain.y + wpMain.height;
\r
2345 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2346 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2347 squareSize = sizeInfo[boardSize].squareSize;
\r
2348 lineGap = sizeInfo[boardSize].lineGap;
\r
2349 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2350 border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;
\r
2352 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2353 lineGap = appData.overrideLineGap;
\r
2356 if (tinyLayout != oldTinyLayout) {
\r
2357 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2359 style &= ~WS_SYSMENU;
\r
2360 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2361 "&Minimize\tCtrl+F4");
\r
2363 style |= WS_SYSMENU;
\r
2364 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2366 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2368 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2369 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2370 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2372 DrawMenuBar(hwndMain);
\r
2375 boardWidth = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;
\r
2376 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;
\r
2378 /* Get text area sizes */
\r
2379 hdc = GetDC(hwndMain);
\r
2380 if (appData.clockMode) {
\r
2381 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2383 snprintf(buf, MSG_SIZ, _("White"));
\r
2385 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2386 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2387 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2388 str = _("We only care about the height here");
\r
2389 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2390 SelectObject(hdc, oldFont);
\r
2391 ReleaseDC(hwndMain, hdc);
\r
2393 /* Compute where everything goes */
\r
2394 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2395 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2396 logoHeight = 2*clockSize.cy;
\r
2397 leftLogoRect.left = OUTER_MARGIN;
\r
2398 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2399 leftLogoRect.top = OUTER_MARGIN;
\r
2400 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2402 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2403 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2404 rightLogoRect.top = OUTER_MARGIN;
\r
2405 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2408 whiteRect.left = leftLogoRect.right;
\r
2409 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2410 whiteRect.top = OUTER_MARGIN;
\r
2411 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2413 blackRect.right = rightLogoRect.left;
\r
2414 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2415 blackRect.top = whiteRect.top;
\r
2416 blackRect.bottom = whiteRect.bottom;
\r
2418 whiteRect.left = OUTER_MARGIN;
\r
2419 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2420 whiteRect.top = OUTER_MARGIN;
\r
2421 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2423 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2424 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2425 blackRect.top = whiteRect.top;
\r
2426 blackRect.bottom = whiteRect.bottom;
\r
2428 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2431 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2432 if (appData.showButtonBar) {
\r
2433 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2434 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2436 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2438 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2439 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2441 boardRect.left = OUTER_MARGIN;
\r
2442 boardRect.right = boardRect.left + boardWidth;
\r
2443 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2444 boardRect.bottom = boardRect.top + boardHeight;
\r
2446 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2447 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2448 oldTinyLayout = tinyLayout;
\r
2449 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2450 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2451 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2452 winW *= 1 + twoBoards;
\r
2453 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2454 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2455 wpMain.height = winH; // without disturbing window attachments
\r
2456 GetWindowRect(hwndMain, &wrect);
\r
2457 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2458 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2460 // [HGM] placement: let attached windows follow size change.
\r
2461 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2462 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2463 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2464 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2465 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2467 /* compensate if menu bar wrapped */
\r
2468 GetClientRect(hwndMain, &crect);
\r
2469 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2470 wpMain.height += offby;
\r
2472 case WMSZ_TOPLEFT:
\r
2473 SetWindowPos(hwndMain, NULL,
\r
2474 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2475 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2478 case WMSZ_TOPRIGHT:
\r
2480 SetWindowPos(hwndMain, NULL,
\r
2481 wrect.left, wrect.bottom - wpMain.height,
\r
2482 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2485 case WMSZ_BOTTOMLEFT:
\r
2487 SetWindowPos(hwndMain, NULL,
\r
2488 wrect.right - wpMain.width, wrect.top,
\r
2489 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2492 case WMSZ_BOTTOMRIGHT:
\r
2496 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2497 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2502 for (i = 0; i < N_BUTTONS; i++) {
\r
2503 if (buttonDesc[i].hwnd != NULL) {
\r
2504 DestroyWindow(buttonDesc[i].hwnd);
\r
2505 buttonDesc[i].hwnd = NULL;
\r
2507 if (appData.showButtonBar) {
\r
2508 buttonDesc[i].hwnd =
\r
2509 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2510 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2511 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2512 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2513 (HMENU) buttonDesc[i].id,
\r
2514 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2516 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2517 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2518 MAKELPARAM(FALSE, 0));
\r
2520 if (buttonDesc[i].id == IDM_Pause)
\r
2521 hwndPause = buttonDesc[i].hwnd;
\r
2522 buttonDesc[i].wndproc = (WNDPROC)
\r
2523 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2526 if (gridPen != NULL) DeleteObject(gridPen);
\r
2527 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2528 if (premovePen != NULL) DeleteObject(premovePen);
\r
2529 if (lineGap != 0) {
\r
2530 logbrush.lbStyle = BS_SOLID;
\r
2531 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2533 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2534 lineGap, &logbrush, 0, NULL);
\r
2535 logbrush.lbColor = highlightSquareColor;
\r
2537 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2538 lineGap, &logbrush, 0, NULL);
\r
2540 logbrush.lbColor = premoveHighlightColor;
\r
2542 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2543 lineGap, &logbrush, 0, NULL);
\r
2545 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2546 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2547 gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;
\r
2548 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2549 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;
\r
2550 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2551 BOARD_WIDTH * (squareSize + lineGap) + border;
\r
2552 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2554 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2555 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;
\r
2556 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2557 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2558 lineGap / 2 + (i * (squareSize + lineGap)) + border;
\r
2559 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2560 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;
\r
2561 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2565 /* [HGM] Licensing requirement */
\r
2567 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2570 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2572 GothicPopUp( "", VariantNormal);
\r
2575 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2577 /* Load piece bitmaps for this board size */
\r
2578 for (i=0; i<=2; i++) {
\r
2579 for (piece = WhitePawn;
\r
2580 (int) piece < (int) BlackPawn;
\r
2581 piece = (ChessSquare) ((int) piece + 1)) {
\r
2582 if (pieceBitmap[i][piece] != NULL)
\r
2583 DeleteObject(pieceBitmap[i][piece]);
\r
2587 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2588 // Orthodox Chess pieces
\r
2589 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2590 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2591 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2592 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2593 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2594 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2595 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2596 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2597 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2598 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2599 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2600 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2601 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2602 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2603 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2604 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2605 // in Shogi, Hijack the unused Queen for Lance
\r
2606 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2607 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2608 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2610 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2611 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2612 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2615 if(squareSize <= 72 && squareSize >= 33) {
\r
2616 /* A & C are available in most sizes now */
\r
2617 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2618 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2619 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2620 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2621 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2622 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2623 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2624 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2625 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2626 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2627 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2628 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2629 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2630 } else { // Smirf-like
\r
2631 if(gameInfo.variant == VariantSChess) {
\r
2632 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2633 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2634 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2636 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2637 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2638 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2641 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2642 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2643 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2644 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2645 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2646 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2647 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2648 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2649 } else { // WinBoard standard
\r
2650 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2651 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2652 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2657 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2658 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2659 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2660 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2661 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2662 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2663 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2664 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2665 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2666 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2667 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2668 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2669 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2670 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2671 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2672 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2673 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2674 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2675 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2676 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2677 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2678 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2679 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2680 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2681 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2682 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2683 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2684 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2685 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2686 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2687 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2688 pieceBitmap[0][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "s");
\r
2689 pieceBitmap[1][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "o");
\r
2690 pieceBitmap[2][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "w");
\r
2692 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2693 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2694 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2695 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2696 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2697 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2698 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2699 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2700 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2701 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2702 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2703 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2704 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2706 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2707 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2708 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2709 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2710 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2711 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2712 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2713 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2714 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2715 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2716 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2717 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2720 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2721 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2722 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2723 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2724 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2725 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2726 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2727 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2728 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2729 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2730 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2731 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2732 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2733 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2734 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2738 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2739 /* special Shogi support in this size */
\r
2740 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2741 for (piece = WhitePawn;
\r
2742 (int) piece < (int) BlackPawn;
\r
2743 piece = (ChessSquare) ((int) piece + 1)) {
\r
2744 if (pieceBitmap[i][piece] != NULL)
\r
2745 DeleteObject(pieceBitmap[i][piece]);
\r
2748 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2749 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2750 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2751 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2752 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2753 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2754 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2755 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2756 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2757 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2758 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2759 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2760 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2761 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2762 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2763 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2764 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2765 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2766 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2767 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2768 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2769 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2770 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2771 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2772 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2773 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2774 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2775 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2776 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2777 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2778 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2779 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2780 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2781 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2782 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2783 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2784 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2785 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2786 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2787 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2788 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2789 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2795 PieceBitmap(ChessSquare p, int kind)
\r
2797 if ((int) p >= (int) BlackPawn)
\r
2798 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2800 return pieceBitmap[kind][(int) p];
\r
2803 /***************************************************************/
\r
2805 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2806 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2808 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2809 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2813 SquareToPos(int row, int column, int * x, int * y)
\r
2816 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
2817 *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;
\r
2819 *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;
\r
2820 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
2825 DrawCoordsOnDC(HDC hdc)
\r
2827 static char files[] = "0123456789012345678901221098765432109876543210";
\r
2828 static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";
\r
2829 char str[2] = { NULLCHAR, NULLCHAR };
\r
2830 int oldMode, oldAlign, x, y, start, i;
\r
2834 if (!appData.showCoords)
\r
2837 start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;
\r
2839 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2840 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2841 oldAlign = GetTextAlign(hdc);
\r
2842 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2844 y = boardRect.top + lineGap;
\r
2845 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2848 SetTextAlign(hdc, TA_RIGHT|TA_TOP);
\r
2849 x += border - lineGap - 4; y += squareSize - 6;
\r
2851 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2852 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2853 str[0] = files[start + i];
\r
2854 ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);
\r
2855 y += squareSize + lineGap;
\r
2858 start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;
\r
2861 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2862 x += -border + 4; y += border - squareSize + 6;
\r
2864 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2865 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2866 str[0] = ranks[start + i];
\r
2867 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2868 x += squareSize + lineGap;
\r
2871 SelectObject(hdc, oldBrush);
\r
2872 SetBkMode(hdc, oldMode);
\r
2873 SetTextAlign(hdc, oldAlign);
\r
2874 SelectObject(hdc, oldFont);
\r
2878 DrawGridOnDC(HDC hdc)
\r
2882 if (lineGap != 0) {
\r
2883 oldPen = SelectObject(hdc, gridPen);
\r
2884 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2885 SelectObject(hdc, oldPen);
\r
2889 #define HIGHLIGHT_PEN 0
\r
2890 #define PREMOVE_PEN 1
\r
2893 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2896 HPEN oldPen, hPen;
\r
2897 if (lineGap == 0) return;
\r
2899 x1 = boardRect.left +
\r
2900 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;
\r
2901 y1 = boardRect.top +
\r
2902 lineGap/2 + y * (squareSize + lineGap) + border;
\r
2904 x1 = boardRect.left +
\r
2905 lineGap/2 + x * (squareSize + lineGap) + border;
\r
2906 y1 = boardRect.top +
\r
2907 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;
\r
2909 hPen = pen ? premovePen : highlightPen;
\r
2910 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2911 MoveToEx(hdc, x1, y1, NULL);
\r
2912 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2913 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2914 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2915 LineTo(hdc, x1, y1);
\r
2916 SelectObject(hdc, oldPen);
\r
2920 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2923 for (i=0; i<2; i++) {
\r
2924 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2925 DrawHighlightOnDC(hdc, TRUE,
\r
2926 h->sq[i].x, h->sq[i].y,
\r
2931 /* Note: sqcolor is used only in monoMode */
\r
2932 /* Note that this code is largely duplicated in woptions.c,
\r
2933 function DrawSampleSquare, so that needs to be updated too */
\r
2935 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2937 HBITMAP oldBitmap;
\r
2941 if (appData.blindfold) return;
\r
2943 /* [AS] Use font-based pieces if needed */
\r
2944 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2945 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2946 CreatePiecesFromFont();
\r
2948 if( fontBitmapSquareSize == squareSize ) {
\r
2949 int index = TranslatePieceToFontPiece(piece);
\r
2951 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2953 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2954 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2958 squareSize, squareSize,
\r
2963 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2965 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2966 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2970 squareSize, squareSize,
\r
2979 if (appData.monoMode) {
\r
2980 SelectObject(tmphdc, PieceBitmap(piece,
\r
2981 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2982 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2983 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2985 HBRUSH xBrush = whitePieceBrush;
\r
2986 tmpSize = squareSize;
\r
2987 if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);
\r
2989 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2990 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2991 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2992 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2993 x += (squareSize - minorSize)>>1;
\r
2994 y += squareSize - minorSize - 2;
\r
2995 tmpSize = minorSize;
\r
2997 if (color || appData.allWhite ) {
\r
2998 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
3000 oldBrush = SelectObject(hdc, xBrush);
\r
3001 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
3002 if(appData.upsideDown && color==flipView)
\r
3003 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
3005 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
3006 /* Use black for outline of white pieces */
\r
3007 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
3008 if(appData.upsideDown && color==flipView)
\r
3009 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
3011 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
3012 } else if(appData.pieceDirectory[0]) {
\r
3013 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
3014 oldBrush = SelectObject(hdc, xBrush);
\r
3015 if(appData.upsideDown && color==flipView)
\r
3016 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
3018 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
3019 SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
3020 if(appData.upsideDown && color==flipView)
\r
3021 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
3023 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
3025 /* Use square color for details of black pieces */
\r
3026 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
3027 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
3028 if(appData.upsideDown && !flipView)
\r
3029 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
3031 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
3033 SelectObject(hdc, oldBrush);
\r
3034 SelectObject(tmphdc, oldBitmap);
\r
3038 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
3039 int GetBackTextureMode( int algo )
\r
3041 int result = BACK_TEXTURE_MODE_DISABLED;
\r
3045 case BACK_TEXTURE_MODE_PLAIN:
\r
3046 result = 1; /* Always use identity map */
\r
3048 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
3049 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
3057 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
3058 to handle redraws cleanly (as random numbers would always be different).
\r
3060 VOID RebuildTextureSquareInfo()
\r
3070 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
3072 if( liteBackTexture != NULL ) {
\r
3073 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3074 lite_w = bi.bmWidth;
\r
3075 lite_h = bi.bmHeight;
\r
3079 if( darkBackTexture != NULL ) {
\r
3080 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3081 dark_w = bi.bmWidth;
\r
3082 dark_h = bi.bmHeight;
\r
3086 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
3087 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
3088 if( (col + row) & 1 ) {
\r
3090 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
3091 if( lite_w >= squareSize*BOARD_WIDTH )
\r
3092 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
3094 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
3095 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
3096 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
3098 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
3099 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
3104 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
3105 if( dark_w >= squareSize*BOARD_WIDTH )
\r
3106 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
3108 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
3109 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
3110 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
3112 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
3113 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
3120 /* [AS] Arrow highlighting support */
\r
3122 static double A_WIDTH = 5; /* Width of arrow body */
\r
3124 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
3125 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
3127 static double Sqr( double x )
\r
3132 static int Round( double x )
\r
3134 return (int) (x + 0.5);
\r
3137 /* Draw an arrow between two points using current settings */
\r
3138 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
3141 double dx, dy, j, k, x, y;
\r
3143 if( d_x == s_x ) {
\r
3144 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3146 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
3149 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
3150 arrow[1].y = d_y - h;
\r
3152 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3153 arrow[2].y = d_y - h;
\r
3158 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3159 arrow[5].y = d_y - h;
\r
3161 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3162 arrow[4].y = d_y - h;
\r
3164 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3167 else if( d_y == s_y ) {
\r
3168 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3171 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3173 arrow[1].x = d_x - w;
\r
3174 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3176 arrow[2].x = d_x - w;
\r
3177 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3182 arrow[5].x = d_x - w;
\r
3183 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3185 arrow[4].x = d_x - w;
\r
3186 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3189 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3192 /* [AS] Needed a lot of paper for this! :-) */
\r
3193 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3194 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3196 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3198 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3203 arrow[0].x = Round(x - j);
\r
3204 arrow[0].y = Round(y + j*dx);
\r
3206 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3207 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3210 x = (double) d_x - k;
\r
3211 y = (double) d_y - k*dy;
\r
3214 x = (double) d_x + k;
\r
3215 y = (double) d_y + k*dy;
\r
3218 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3220 arrow[6].x = Round(x - j);
\r
3221 arrow[6].y = Round(y + j*dx);
\r
3223 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3224 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3226 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3227 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3232 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3233 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3236 Polygon( hdc, arrow, 7 );
\r
3239 /* [AS] Draw an arrow between two squares */
\r
3240 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3242 int s_x, s_y, d_x, d_y;
\r
3249 if( s_col == d_col && s_row == d_row ) {
\r
3253 /* Get source and destination points */
\r
3254 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3255 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3258 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3260 else if( d_y < s_y ) {
\r
3261 d_y += squareSize / 2 + squareSize / 4;
\r
3264 d_y += squareSize / 2;
\r
3268 d_x += squareSize / 2 - squareSize / 4;
\r
3270 else if( d_x < s_x ) {
\r
3271 d_x += squareSize / 2 + squareSize / 4;
\r
3274 d_x += squareSize / 2;
\r
3277 s_x += squareSize / 2;
\r
3278 s_y += squareSize / 2;
\r
3280 /* Adjust width */
\r
3281 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3284 stLB.lbStyle = BS_SOLID;
\r
3285 stLB.lbColor = appData.highlightArrowColor;
\r
3288 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3289 holdpen = SelectObject( hdc, hpen );
\r
3290 hbrush = CreateBrushIndirect( &stLB );
\r
3291 holdbrush = SelectObject( hdc, hbrush );
\r
3293 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3295 SelectObject( hdc, holdpen );
\r
3296 SelectObject( hdc, holdbrush );
\r
3297 DeleteObject( hpen );
\r
3298 DeleteObject( hbrush );
\r
3301 BOOL HasHighlightInfo()
\r
3303 BOOL result = FALSE;
\r
3305 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3306 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3317 BOOL IsDrawArrowEnabled()
\r
3319 BOOL result = FALSE;
\r
3321 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3328 VOID DrawArrowHighlight( HDC hdc )
\r
3330 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3331 DrawArrowBetweenSquares( hdc,
\r
3332 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3333 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3337 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3339 HRGN result = NULL;
\r
3341 if( HasHighlightInfo() ) {
\r
3342 int x1, y1, x2, y2;
\r
3343 int sx, sy, dx, dy;
\r
3345 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3346 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3348 sx = MIN( x1, x2 );
\r
3349 sy = MIN( y1, y2 );
\r
3350 dx = MAX( x1, x2 ) + squareSize;
\r
3351 dy = MAX( y1, y2 ) + squareSize;
\r
3353 result = CreateRectRgn( sx, sy, dx, dy );
\r
3360 Warning: this function modifies the behavior of several other functions.
\r
3362 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3363 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3364 repaint is scattered all over the place, which is not good for features such as
\r
3365 "arrow highlighting" that require a full repaint of the board.
\r
3367 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3368 user interaction, when speed is not so important) but especially to avoid errors
\r
3369 in the displayed graphics.
\r
3371 In such patched places, I always try refer to this function so there is a single
\r
3372 place to maintain knowledge.
\r
3374 To restore the original behavior, just return FALSE unconditionally.
\r
3376 BOOL IsFullRepaintPreferrable()
\r
3378 BOOL result = FALSE;
\r
3380 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3381 /* Arrow may appear on the board */
\r
3389 This function is called by DrawPosition to know whether a full repaint must
\r
3392 Only DrawPosition may directly call this function, which makes use of
\r
3393 some state information. Other function should call DrawPosition specifying
\r
3394 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3396 BOOL DrawPositionNeedsFullRepaint()
\r
3398 BOOL result = FALSE;
\r
3401 Probably a slightly better policy would be to trigger a full repaint
\r
3402 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3403 but animation is fast enough that it's difficult to notice.
\r
3405 if( animInfo.piece == EmptySquare ) {
\r
3406 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3414 static HBITMAP borderBitmap;
\r
3417 DrawBackgroundOnDC(HDC hdc)
\r
3423 static char oldBorder[MSG_SIZ];
\r
3424 int w = 600, h = 600, mode;
\r
3426 if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid
\r
3427 strncpy(oldBorder, appData.border, MSG_SIZ-1);
\r
3428 borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
3430 if(borderBitmap == NULL) { // loading failed, use white
\r
3431 FillRect( hdc, &boardRect, whitePieceBrush );
\r
3434 tmphdc = CreateCompatibleDC(hdc);
\r
3435 hbm = SelectObject(tmphdc, borderBitmap);
\r
3436 if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {
\r
3440 mode = SetStretchBltMode(hdc, COLORONCOLOR);
\r
3441 StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left,
\r
3442 boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3443 SetStretchBltMode(hdc, mode);
\r
3444 SelectObject(tmphdc, hbm);
\r
3449 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3451 int row, column, x, y, square_color, piece_color;
\r
3452 ChessSquare piece;
\r
3454 HDC texture_hdc = NULL;
\r
3456 /* [AS] Initialize background textures if needed */
\r
3457 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3458 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3459 if( backTextureSquareSize != squareSize
\r
3460 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3461 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3462 backTextureSquareSize = squareSize;
\r
3463 RebuildTextureSquareInfo();
\r
3466 texture_hdc = CreateCompatibleDC( hdc );
\r
3469 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3470 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3472 SquareToPos(row, column, &x, &y);
\r
3474 piece = board[row][column];
\r
3476 square_color = ((column + row) % 2) == 1;
\r
3477 if( gameInfo.variant == VariantXiangqi ) {
\r
3478 square_color = !InPalace(row, column);
\r
3479 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3480 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3482 piece_color = (int) piece < (int) BlackPawn;
\r
3485 /* [HGM] holdings file: light square or black */
\r
3486 if(column == BOARD_LEFT-2) {
\r
3487 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3490 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3494 if(column == BOARD_RGHT + 1 ) {
\r
3495 if( row < gameInfo.holdingsSize )
\r
3498 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3502 if(column == BOARD_LEFT-1 ) /* left align */
\r
3503 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3504 else if( column == BOARD_RGHT) /* right align */
\r
3505 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3506 else if( piece == DarkSquare) DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3508 if (appData.monoMode) {
\r
3509 if (piece == EmptySquare) {
\r
3510 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3511 square_color ? WHITENESS : BLACKNESS);
\r
3513 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3516 else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {
\r
3517 /* [AS] Draw the square using a texture bitmap */
\r
3518 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3519 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3520 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3523 squareSize, squareSize,
\r
3526 backTextureSquareInfo[r][c].mode,
\r
3527 backTextureSquareInfo[r][c].x,
\r
3528 backTextureSquareInfo[r][c].y );
\r
3530 SelectObject( texture_hdc, hbm );
\r
3532 if (piece != EmptySquare) {
\r
3533 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3537 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3539 oldBrush = SelectObject(hdc, brush );
\r
3540 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3541 SelectObject(hdc, oldBrush);
\r
3542 if (piece != EmptySquare)
\r
3543 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3548 if( texture_hdc != NULL ) {
\r
3549 DeleteDC( texture_hdc );
\r
3553 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3554 void fputDW(FILE *f, int x)
\r
3556 fputc(x & 255, f);
\r
3557 fputc(x>>8 & 255, f);
\r
3558 fputc(x>>16 & 255, f);
\r
3559 fputc(x>>24 & 255, f);
\r
3562 #define MAX_CLIPS 200 /* more than enough */
\r
3565 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3567 // HBITMAP bufferBitmap;
\r
3572 int w = 100, h = 50;
\r
3574 if(logo == NULL) {
\r
3575 if(!logoHeight) return;
\r
3576 FillRect( hdc, &logoRect, whitePieceBrush );
\r
3578 // GetClientRect(hwndMain, &Rect);
\r
3579 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3580 // Rect.bottom-Rect.top+1);
\r
3581 tmphdc = CreateCompatibleDC(hdc);
\r
3582 hbm = SelectObject(tmphdc, logo);
\r
3583 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3587 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3588 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3589 SelectObject(tmphdc, hbm);
\r
3597 HDC hdc = GetDC(hwndMain);
\r
3598 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3599 if(appData.autoLogo) {
\r
3601 switch(gameMode) { // pick logos based on game mode
\r
3602 case IcsObserving:
\r
3603 whiteLogo = second.programLogo; // ICS logo
\r
3604 blackLogo = second.programLogo;
\r
3607 case IcsPlayingWhite:
\r
3608 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3609 blackLogo = second.programLogo; // ICS logo
\r
3611 case IcsPlayingBlack:
\r
3612 whiteLogo = second.programLogo; // ICS logo
\r
3613 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3615 case TwoMachinesPlay:
\r
3616 if(first.twoMachinesColor[0] == 'b') {
\r
3617 whiteLogo = second.programLogo;
\r
3618 blackLogo = first.programLogo;
\r
3621 case MachinePlaysWhite:
\r
3622 blackLogo = userLogo;
\r
3624 case MachinePlaysBlack:
\r
3625 whiteLogo = userLogo;
\r
3626 blackLogo = first.programLogo;
\r
3629 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3630 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3631 ReleaseDC(hwndMain, hdc);
\r
3636 UpdateLogos(int display)
\r
3637 { // called after loading new engine(s), in tourney or from menu
\r
3638 LoadLogo(&first, 0, FALSE);
\r
3639 LoadLogo(&second, 1, appData.icsActive);
\r
3640 InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos
\r
3641 if(display) DisplayLogos();
\r
3644 static HDC hdcSeek;
\r
3646 // [HGM] seekgraph
\r
3647 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3650 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3651 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3652 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3653 SelectObject( hdcSeek, hp );
\r
3656 // front-end wrapper for drawing functions to do rectangles
\r
3657 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3662 if (hdcSeek == NULL) {
\r
3663 hdcSeek = GetDC(hwndMain);
\r
3664 if (!appData.monoMode) {
\r
3665 SelectPalette(hdcSeek, hPal, FALSE);
\r
3666 RealizePalette(hdcSeek);
\r
3669 hp = SelectObject( hdcSeek, gridPen );
\r
3670 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3671 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3672 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3673 SelectObject( hdcSeek, hp );
\r
3676 // front-end wrapper for putting text in graph
\r
3677 void DrawSeekText(char *buf, int x, int y)
\r
3680 SetBkMode( hdcSeek, TRANSPARENT );
\r
3681 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3682 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3685 void DrawSeekDot(int x, int y, int color)
\r
3687 int square = color & 0x80;
\r
3688 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3689 color == 0 ? markerBrush[1] : color == 1 ? darkSquareBrush : explodeBrush);
\r
3692 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3693 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3695 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3696 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3697 SelectObject(hdcSeek, oldBrush);
\r
3700 void DrawSeekOpen()
\r
3704 void DrawSeekClose()
\r
3709 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3711 static Board lastReq[2], lastDrawn[2];
\r
3712 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3713 static int lastDrawnFlipView = 0;
\r
3714 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3715 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3718 HBITMAP bufferBitmap;
\r
3719 HBITMAP oldBitmap;
\r
3721 HRGN clips[MAX_CLIPS];
\r
3722 ChessSquare dragged_piece = EmptySquare;
\r
3723 int nr = twoBoards*partnerUp;
\r
3725 /* I'm undecided on this - this function figures out whether a full
\r
3726 * repaint is necessary on its own, so there's no real reason to have the
\r
3727 * caller tell it that. I think this can safely be set to FALSE - but
\r
3728 * if we trust the callers not to request full repaints unnessesarily, then
\r
3729 * we could skip some clipping work. In other words, only request a full
\r
3730 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3731 * gamestart and similar) --Hawk
\r
3733 Boolean fullrepaint = repaint;
\r
3735 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3737 if( DrawPositionNeedsFullRepaint() ) {
\r
3738 fullrepaint = TRUE;
\r
3741 if (board == NULL) {
\r
3742 if (!lastReqValid[nr]) {
\r
3745 board = lastReq[nr];
\r
3747 CopyBoard(lastReq[nr], board);
\r
3748 lastReqValid[nr] = 1;
\r
3751 if (doingSizing) {
\r
3755 if (IsIconic(hwndMain)) {
\r
3759 if (hdc == NULL) {
\r
3760 hdc = GetDC(hwndMain);
\r
3761 if (!appData.monoMode) {
\r
3762 SelectPalette(hdc, hPal, FALSE);
\r
3763 RealizePalette(hdc);
\r
3767 releaseDC = FALSE;
\r
3770 /* Create some work-DCs */
\r
3771 hdcmem = CreateCompatibleDC(hdc);
\r
3772 tmphdc = CreateCompatibleDC(hdc);
\r
3774 /* If dragging is in progress, we temporarely remove the piece */
\r
3775 /* [HGM] or temporarily decrease count if stacked */
\r
3776 /* !! Moved to before board compare !! */
\r
3777 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3778 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3779 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3780 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3781 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3783 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3784 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3785 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3787 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3790 /* Figure out which squares need updating by comparing the
\r
3791 * newest board with the last drawn board and checking if
\r
3792 * flipping has changed.
\r
3794 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3795 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3796 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3797 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3798 SquareToPos(row, column, &x, &y);
\r
3799 clips[num_clips++] =
\r
3800 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3804 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3805 for (i=0; i<2; i++) {
\r
3806 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3807 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3808 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3809 lastDrawnHighlight.sq[i].y >= 0) {
\r
3810 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3811 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3812 clips[num_clips++] =
\r
3813 CreateRectRgn(x - lineGap, y - lineGap,
\r
3814 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3816 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3817 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3818 clips[num_clips++] =
\r
3819 CreateRectRgn(x - lineGap, y - lineGap,
\r
3820 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3824 for (i=0; i<2; i++) {
\r
3825 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3826 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3827 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3828 lastDrawnPremove.sq[i].y >= 0) {
\r
3829 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3830 lastDrawnPremove.sq[i].x, &x, &y);
\r
3831 clips[num_clips++] =
\r
3832 CreateRectRgn(x - lineGap, y - lineGap,
\r
3833 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3835 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3836 premoveHighlightInfo.sq[i].y >= 0) {
\r
3837 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3838 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3839 clips[num_clips++] =
\r
3840 CreateRectRgn(x - lineGap, y - lineGap,
\r
3841 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3845 } else { // nr == 1
\r
3846 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3847 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3848 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3849 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3850 for (i=0; i<2; i++) {
\r
3851 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3852 partnerHighlightInfo.sq[i].y >= 0) {
\r
3853 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3854 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3855 clips[num_clips++] =
\r
3856 CreateRectRgn(x - lineGap, y - lineGap,
\r
3857 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3859 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3860 oldPartnerHighlight.sq[i].y >= 0) {
\r
3861 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3862 oldPartnerHighlight.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
3870 fullrepaint = TRUE;
\r
3873 /* Create a buffer bitmap - this is the actual bitmap
\r
3874 * being written to. When all the work is done, we can
\r
3875 * copy it to the real DC (the screen). This avoids
\r
3876 * the problems with flickering.
\r
3878 GetClientRect(hwndMain, &Rect);
\r
3879 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3880 Rect.bottom-Rect.top+1);
\r
3881 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3882 if (!appData.monoMode) {
\r
3883 SelectPalette(hdcmem, hPal, FALSE);
\r
3886 /* Create clips for dragging */
\r
3887 if (!fullrepaint) {
\r
3888 if (dragInfo.from.x >= 0) {
\r
3889 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3890 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3892 if (dragInfo.start.x >= 0) {
\r
3893 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3894 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3896 if (dragInfo.pos.x >= 0) {
\r
3897 x = dragInfo.pos.x - squareSize / 2;
\r
3898 y = dragInfo.pos.y - squareSize / 2;
\r
3899 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3901 if (dragInfo.lastpos.x >= 0) {
\r
3902 x = dragInfo.lastpos.x - squareSize / 2;
\r
3903 y = dragInfo.lastpos.y - squareSize / 2;
\r
3904 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3908 /* Are we animating a move?
\r
3910 * - remove the piece from the board (temporarely)
\r
3911 * - calculate the clipping region
\r
3913 if (!fullrepaint) {
\r
3914 if (animInfo.piece != EmptySquare) {
\r
3915 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3916 x = boardRect.left + animInfo.lastpos.x;
\r
3917 y = boardRect.top + animInfo.lastpos.y;
\r
3918 x2 = boardRect.left + animInfo.pos.x;
\r
3919 y2 = boardRect.top + animInfo.pos.y;
\r
3920 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3921 /* Slight kludge. The real problem is that after AnimateMove is
\r
3922 done, the position on the screen does not match lastDrawn.
\r
3923 This currently causes trouble only on e.p. captures in
\r
3924 atomic, where the piece moves to an empty square and then
\r
3925 explodes. The old and new positions both had an empty square
\r
3926 at the destination, but animation has drawn a piece there and
\r
3927 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3928 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3932 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3933 if (num_clips == 0)
\r
3934 fullrepaint = TRUE;
\r
3936 /* Set clipping on the memory DC */
\r
3937 if (!fullrepaint) {
\r
3938 SelectClipRgn(hdcmem, clips[0]);
\r
3939 for (x = 1; x < num_clips; x++) {
\r
3940 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3941 abort(); // this should never ever happen!
\r
3945 /* Do all the drawing to the memory DC */
\r
3946 if(explodeInfo.radius) { // [HGM] atomic
\r
3948 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3949 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3950 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3951 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3952 x += squareSize/2;
\r
3953 y += squareSize/2;
\r
3954 if(!fullrepaint) {
\r
3955 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3956 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3958 DrawGridOnDC(hdcmem);
\r
3959 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3960 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3961 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3962 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3963 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3964 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3965 SelectObject(hdcmem, oldBrush);
\r
3967 if(border) DrawBackgroundOnDC(hdcmem);
\r
3968 DrawGridOnDC(hdcmem);
\r
3969 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3970 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3971 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3973 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3974 oldPartnerHighlight = partnerHighlightInfo;
\r
3976 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3978 if(nr == 0) // [HGM] dual: markers only on left board
\r
3979 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3980 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3981 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3982 HBRUSH oldBrush = SelectObject(hdcmem, markerBrush[marker[row][column]-1]);
\r
3983 SquareToPos(row, column, &x, &y);
\r
3984 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3985 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3986 SelectObject(hdcmem, oldBrush);
\r
3991 if( appData.highlightMoveWithArrow ) {
\r
3992 DrawArrowHighlight(hdcmem);
\r
3995 DrawCoordsOnDC(hdcmem);
\r
3997 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3998 /* to make sure lastDrawn contains what is actually drawn */
\r
4000 /* Put the dragged piece back into place and draw it (out of place!) */
\r
4001 if (dragged_piece != EmptySquare) {
\r
4002 /* [HGM] or restack */
\r
4003 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
4004 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
4006 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
4007 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
4009 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
4010 x = dragInfo.pos.x - squareSize / 2;
\r
4011 y = dragInfo.pos.y - squareSize / 2;
\r
4012 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
4013 ((int) dragInfo.piece < (int) BlackPawn),
\r
4014 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
4017 /* Put the animated piece back into place and draw it */
\r
4018 if (animInfo.piece != EmptySquare) {
\r
4019 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
4020 x = boardRect.left + animInfo.pos.x;
\r
4021 y = boardRect.top + animInfo.pos.y;
\r
4022 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
4023 ((int) animInfo.piece < (int) BlackPawn),
\r
4024 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
4027 /* Release the bufferBitmap by selecting in the old bitmap
\r
4028 * and delete the memory DC
\r
4030 SelectObject(hdcmem, oldBitmap);
\r
4033 /* Set clipping on the target DC */
\r
4034 if (!fullrepaint) {
\r
4035 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
4037 GetRgnBox(clips[x], &rect);
\r
4038 DeleteObject(clips[x]);
\r
4039 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
4040 rect.right + wpMain.width/2, rect.bottom);
\r
4042 SelectClipRgn(hdc, clips[0]);
\r
4043 for (x = 1; x < num_clips; x++) {
\r
4044 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
4045 abort(); // this should never ever happen!
\r
4049 /* Copy the new bitmap onto the screen in one go.
\r
4050 * This way we avoid any flickering
\r
4052 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
4053 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
4054 boardRect.right - boardRect.left,
\r
4055 boardRect.bottom - boardRect.top,
\r
4056 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
4057 if(saveDiagFlag) {
\r
4058 BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData;
\r
4059 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
4061 GetObject(bufferBitmap, sizeof(b), &b);
\r
4062 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\r
4063 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
4064 bih.biWidth = b.bmWidth;
\r
4065 bih.biHeight = b.bmHeight;
\r
4067 bih.biBitCount = b.bmBitsPixel;
\r
4068 bih.biCompression = 0;
\r
4069 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
4070 bih.biXPelsPerMeter = 0;
\r
4071 bih.biYPelsPerMeter = 0;
\r
4072 bih.biClrUsed = 0;
\r
4073 bih.biClrImportant = 0;
\r
4074 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
4075 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
4076 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
4077 // fprintf(diagFile, "%8x\n", (int) pData);
\r
4079 wb = b.bmWidthBytes;
\r
4081 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
4082 int k = ((int*) pData)[i];
\r
4083 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4084 if(j >= 16) break;
\r
4086 if(j >= nrColors) nrColors = j+1;
\r
4088 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
4090 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
4091 for(w=0; w<(wb>>2); w+=2) {
\r
4092 int k = ((int*) pData)[(wb*i>>2) + w];
\r
4093 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4094 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
4095 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
4096 pData[p++] = m | j<<4;
\r
4098 while(p&3) pData[p++] = 0;
\r
4101 wb = ((wb+31)>>5)<<2;
\r
4103 // write BITMAPFILEHEADER
\r
4104 fprintf(diagFile, "BM");
\r
4105 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
4106 fputDW(diagFile, 0);
\r
4107 fputDW(diagFile, 0x36 + (fac?64:0));
\r
4108 // write BITMAPINFOHEADER
\r
4109 fputDW(diagFile, 40);
\r
4110 fputDW(diagFile, b.bmWidth);
\r
4111 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
4112 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
4113 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
4114 fputDW(diagFile, 0);
\r
4115 fputDW(diagFile, 0);
\r
4116 fputDW(diagFile, 0);
\r
4117 fputDW(diagFile, 0);
\r
4118 fputDW(diagFile, 0);
\r
4119 fputDW(diagFile, 0);
\r
4120 // write color table
\r
4122 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
4123 // write bitmap data
\r
4124 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
4125 fputc(pData[i], diagFile);
\r
4130 SelectObject(tmphdc, oldBitmap);
\r
4132 /* Massive cleanup */
\r
4133 for (x = 0; x < num_clips; x++)
\r
4134 DeleteObject(clips[x]);
\r
4137 DeleteObject(bufferBitmap);
\r
4140 ReleaseDC(hwndMain, hdc);
\r
4142 if (lastDrawnFlipView != flipView && nr == 0) {
\r
4144 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
4146 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
4149 /* CopyBoard(lastDrawn, board);*/
\r
4150 lastDrawnHighlight = highlightInfo;
\r
4151 lastDrawnPremove = premoveHighlightInfo;
\r
4152 lastDrawnFlipView = flipView;
\r
4153 lastDrawnValid[nr] = 1;
\r
4156 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
4161 saveDiagFlag = 1; diagFile = f;
\r
4162 HDCDrawPosition(NULL, TRUE, NULL);
\r
4170 /*---------------------------------------------------------------------------*\
\r
4171 | CLIENT PAINT PROCEDURE
\r
4172 | This is the main event-handler for the WM_PAINT message.
\r
4174 \*---------------------------------------------------------------------------*/
\r
4176 PaintProc(HWND hwnd)
\r
4182 if((hdc = BeginPaint(hwnd, &ps))) {
\r
4183 if (IsIconic(hwnd)) {
\r
4184 DrawIcon(hdc, 2, 2, iconCurrent);
\r
4186 if (!appData.monoMode) {
\r
4187 SelectPalette(hdc, hPal, FALSE);
\r
4188 RealizePalette(hdc);
\r
4190 HDCDrawPosition(hdc, 1, NULL);
\r
4191 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
4192 flipView = !flipView; partnerUp = !partnerUp;
\r
4193 HDCDrawPosition(hdc, 1, NULL);
\r
4194 flipView = !flipView; partnerUp = !partnerUp;
\r
4197 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
4198 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
4199 ETO_CLIPPED|ETO_OPAQUE,
\r
4200 &messageRect, messageText, strlen(messageText), NULL);
\r
4201 SelectObject(hdc, oldFont);
\r
4202 DisplayBothClocks();
\r
4205 EndPaint(hwnd,&ps);
\r
4213 * If the user selects on a border boundary, return -1; if off the board,
\r
4214 * return -2. Otherwise map the event coordinate to the square.
\r
4215 * The offset boardRect.left or boardRect.top must already have been
\r
4216 * subtracted from x.
\r
4218 int EventToSquare(x, limit)
\r
4223 if (x < lineGap + border)
\r
4225 x -= lineGap + border;
\r
4226 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4228 x /= (squareSize + lineGap);
\r
4240 DropEnable dropEnables[] = {
\r
4241 { 'P', DP_Pawn, N_("Pawn") },
\r
4242 { 'N', DP_Knight, N_("Knight") },
\r
4243 { 'B', DP_Bishop, N_("Bishop") },
\r
4244 { 'R', DP_Rook, N_("Rook") },
\r
4245 { 'Q', DP_Queen, N_("Queen") },
\r
4249 SetupDropMenu(HMENU hmenu)
\r
4251 int i, count, enable;
\r
4253 extern char white_holding[], black_holding[];
\r
4254 char item[MSG_SIZ];
\r
4256 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4257 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4258 dropEnables[i].piece);
\r
4260 while (p && *p++ == dropEnables[i].piece) count++;
\r
4261 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4262 enable = count > 0 || !appData.testLegality
\r
4263 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4264 && !appData.icsActive);
\r
4265 ModifyMenu(hmenu, dropEnables[i].command,
\r
4266 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4267 dropEnables[i].command, item);
\r
4271 void DragPieceBegin(int x, int y, Boolean instantly)
\r
4273 dragInfo.lastpos.x = boardRect.left + x;
\r
4274 dragInfo.lastpos.y = boardRect.top + y;
\r
4275 if(instantly) dragInfo.pos = dragInfo.lastpos;
\r
4276 dragInfo.from.x = fromX;
\r
4277 dragInfo.from.y = fromY;
\r
4278 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4279 dragInfo.start = dragInfo.from;
\r
4280 SetCapture(hwndMain);
\r
4283 void DragPieceEnd(int x, int y)
\r
4286 dragInfo.start.x = dragInfo.start.y = -1;
\r
4287 dragInfo.from = dragInfo.start;
\r
4288 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4291 void ChangeDragPiece(ChessSquare piece)
\r
4293 dragInfo.piece = piece;
\r
4296 /* Event handler for mouse messages */
\r
4298 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4302 static int recursive = 0;
\r
4304 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4307 if (message == WM_MBUTTONUP) {
\r
4308 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4309 to the middle button: we simulate pressing the left button too!
\r
4311 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4312 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4318 pt.x = LOWORD(lParam);
\r
4319 pt.y = HIWORD(lParam);
\r
4320 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4321 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4322 if (!flipView && y >= 0) {
\r
4323 y = BOARD_HEIGHT - 1 - y;
\r
4325 if (flipView && x >= 0) {
\r
4326 x = BOARD_WIDTH - 1 - x;
\r
4329 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4330 controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status
\r
4332 switch (message) {
\r
4333 case WM_LBUTTONDOWN:
\r
4334 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4335 ClockClick(flipClock); break;
\r
4336 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4337 ClockClick(!flipClock); break;
\r
4339 if(dragging) { // [HGM] lion: don't destroy dragging info if we are already dragging
\r
4340 dragInfo.start.x = dragInfo.start.y = -1;
\r
4341 dragInfo.from = dragInfo.start;
\r
4343 if(fromX == -1 && frozen) { // not sure where this is for
\r
4344 fromX = fromY = -1;
\r
4345 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4348 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4349 DrawPosition(TRUE, NULL);
\r
4352 case WM_LBUTTONUP:
\r
4353 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4354 DrawPosition(TRUE, NULL);
\r
4357 case WM_MOUSEMOVE:
\r
4358 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4359 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4360 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4361 if ((appData.animateDragging || appData.highlightDragging)
\r
4362 && (wParam & MK_LBUTTON || dragging == 2)
\r
4363 && dragInfo.from.x >= 0)
\r
4365 BOOL full_repaint = FALSE;
\r
4367 if (appData.animateDragging) {
\r
4368 dragInfo.pos = pt;
\r
4370 if (appData.highlightDragging) {
\r
4371 HoverEvent(highlightInfo.sq[1].x, highlightInfo.sq[1].y, x, y);
\r
4372 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4373 full_repaint = TRUE;
\r
4377 DrawPosition( full_repaint, NULL);
\r
4379 dragInfo.lastpos = dragInfo.pos;
\r
4383 case WM_MOUSEWHEEL: // [DM]
\r
4384 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4385 /* Mouse Wheel is being rolled forward
\r
4386 * Play moves forward
\r
4388 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4389 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4390 /* Mouse Wheel is being rolled backward
\r
4391 * Play moves backward
\r
4393 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4394 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4398 case WM_MBUTTONUP:
\r
4399 case WM_RBUTTONUP:
\r
4401 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4404 case WM_MBUTTONDOWN:
\r
4405 case WM_RBUTTONDOWN:
\r
4408 fromX = fromY = -1;
\r
4409 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4410 dragInfo.start.x = dragInfo.start.y = -1;
\r
4411 dragInfo.from = dragInfo.start;
\r
4412 dragInfo.lastpos = dragInfo.pos;
\r
4413 if (appData.highlightDragging) {
\r
4414 ClearHighlights();
\r
4417 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4418 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4419 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4420 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4421 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4425 DrawPosition(TRUE, NULL);
\r
4427 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4430 if (message == WM_MBUTTONDOWN) {
\r
4431 buttonCount = 3; /* even if system didn't think so */
\r
4432 if (wParam & MK_SHIFT)
\r
4433 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4435 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4436 } else { /* message == WM_RBUTTONDOWN */
\r
4437 /* Just have one menu, on the right button. Windows users don't
\r
4438 think to try the middle one, and sometimes other software steals
\r
4439 it, or it doesn't really exist. */
\r
4440 if(gameInfo.variant != VariantShogi)
\r
4441 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4443 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4447 SetCapture(hwndMain);
\r
4450 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4451 SetupDropMenu(hmenu);
\r
4452 MenuPopup(hwnd, pt, hmenu, -1);
\r
4462 /* Preprocess messages for buttons in main window */
\r
4464 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4466 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4469 for (i=0; i<N_BUTTONS; i++) {
\r
4470 if (buttonDesc[i].id == id) break;
\r
4472 if (i == N_BUTTONS) return 0;
\r
4473 switch (message) {
\r
4478 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4479 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4486 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4489 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4490 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4491 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4492 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4494 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4496 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4497 TypeInEvent((char)wParam);
\r
4503 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4506 static int promoStyle;
\r
4508 /* Process messages for Promotion dialog box */
\r
4510 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4514 switch (message) {
\r
4516 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4517 /* Center the dialog over the application window */
\r
4518 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4519 Translate(hDlg, DLG_PromotionKing);
\r
4520 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4521 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4522 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4523 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4524 SW_SHOW : SW_HIDE);
\r
4525 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4526 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4527 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4528 PieceToChar(WhiteAngel) != '~') ||
\r
4529 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4530 PieceToChar(BlackAngel) != '~') ) ?
\r
4531 SW_SHOW : SW_HIDE);
\r
4532 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4533 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4534 PieceToChar(WhiteMarshall) != '~') ||
\r
4535 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4536 PieceToChar(BlackMarshall) != '~') ) ?
\r
4537 SW_SHOW : SW_HIDE);
\r
4538 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4539 ShowWindow(GetDlgItem(hDlg, PB_Rook), !promoStyle ? SW_SHOW : SW_HIDE);
\r
4540 ShowWindow(GetDlgItem(hDlg, PB_Bishop), !promoStyle ? SW_SHOW : SW_HIDE);
\r
4542 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4543 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4544 SetWindowText(hDlg, "Promote?");
\r
4546 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4547 gameInfo.variant == VariantSuper ?
\r
4548 SW_SHOW : SW_HIDE);
\r
4551 case WM_COMMAND: /* message: received a command */
\r
4552 switch (LOWORD(wParam)) {
\r
4554 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4555 ClearHighlights();
\r
4556 DrawPosition(FALSE, NULL);
\r
4559 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4562 promoChar = promoStyle ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4565 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4566 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4569 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4570 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4572 case PB_Chancellor:
\r
4573 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4575 case PB_Archbishop:
\r
4576 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4579 promoChar = gameInfo.variant == VariantShogi ? '=' : promoStyle ? NULLCHAR :
\r
4580 ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight));
\r
4585 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4586 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4587 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4588 fromX = fromY = -1;
\r
4589 if (!appData.highlightLastMove) {
\r
4590 ClearHighlights();
\r
4591 DrawPosition(FALSE, NULL);
\r
4598 /* Pop up promotion dialog */
\r
4600 PromotionPopup(HWND hwnd)
\r
4604 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4605 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4606 hwnd, (DLGPROC)lpProc);
\r
4607 FreeProcInstance(lpProc);
\r
4611 PromotionPopUp(char choice)
\r
4613 promoStyle = (choice == '+');
\r
4614 DrawPosition(TRUE, NULL);
\r
4615 PromotionPopup(hwndMain);
\r
4619 LoadGameDialog(HWND hwnd, char* title)
\r
4623 char fileTitle[MSG_SIZ];
\r
4624 f = OpenFileDialog(hwnd, "rb", "",
\r
4625 appData.oldSaveStyle ? "gam" : "pgn",
\r
4627 title, &number, fileTitle, NULL);
\r
4629 cmailMsgLoaded = FALSE;
\r
4630 if (number == 0) {
\r
4631 int error = GameListBuild(f);
\r
4633 DisplayError(_("Cannot build game list"), error);
\r
4634 } else if (!ListEmpty(&gameList) &&
\r
4635 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4636 GameListPopUp(f, fileTitle);
\r
4639 GameListDestroy();
\r
4642 LoadGame(f, number, fileTitle, FALSE);
\r
4646 int get_term_width()
\r
4651 HFONT hfont, hold_font;
\r
4656 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4660 // get the text metrics
\r
4661 hdc = GetDC(hText);
\r
4662 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4663 if (consoleCF.dwEffects & CFE_BOLD)
\r
4664 lf.lfWeight = FW_BOLD;
\r
4665 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4666 lf.lfItalic = TRUE;
\r
4667 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4668 lf.lfStrikeOut = TRUE;
\r
4669 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4670 lf.lfUnderline = TRUE;
\r
4671 hfont = CreateFontIndirect(&lf);
\r
4672 hold_font = SelectObject(hdc, hfont);
\r
4673 GetTextMetrics(hdc, &tm);
\r
4674 SelectObject(hdc, hold_font);
\r
4675 DeleteObject(hfont);
\r
4676 ReleaseDC(hText, hdc);
\r
4678 // get the rectangle
\r
4679 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4681 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4684 void UpdateICSWidth(HWND hText)
\r
4686 LONG old_width, new_width;
\r
4688 new_width = get_term_width(hText, FALSE);
\r
4689 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4690 if (new_width != old_width)
\r
4692 ics_update_width(new_width);
\r
4693 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4698 ChangedConsoleFont()
\r
4701 CHARRANGE tmpsel, sel;
\r
4702 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4703 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4704 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4707 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4708 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4709 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4710 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4711 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4712 * size. This was undocumented in the version of MSVC++ that I had
\r
4713 * when I wrote the code, but is apparently documented now.
\r
4715 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4716 cfmt.bCharSet = f->lf.lfCharSet;
\r
4717 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4718 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4719 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4720 /* Why are the following seemingly needed too? */
\r
4721 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4722 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4723 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4725 tmpsel.cpMax = -1; /*999999?*/
\r
4726 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4727 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4728 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4729 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4731 paraf.cbSize = sizeof(paraf);
\r
4732 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4733 paraf.dxStartIndent = 0;
\r
4734 paraf.dxOffset = WRAP_INDENT;
\r
4735 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4736 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4737 UpdateICSWidth(hText);
\r
4740 /*---------------------------------------------------------------------------*\
\r
4742 * Window Proc for main window
\r
4744 \*---------------------------------------------------------------------------*/
\r
4746 /* Process messages for main window, etc. */
\r
4748 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4755 char fileTitle[MSG_SIZ];
\r
4756 static SnapData sd;
\r
4757 static int peek=0;
\r
4759 switch (message) {
\r
4761 case WM_PAINT: /* message: repaint portion of window */
\r
4765 case WM_ERASEBKGND:
\r
4766 if (IsIconic(hwnd)) {
\r
4767 /* Cheat; change the message */
\r
4768 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4770 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4774 case WM_LBUTTONDOWN:
\r
4775 case WM_MBUTTONDOWN:
\r
4776 case WM_RBUTTONDOWN:
\r
4777 case WM_LBUTTONUP:
\r
4778 case WM_MBUTTONUP:
\r
4779 case WM_RBUTTONUP:
\r
4780 case WM_MOUSEMOVE:
\r
4781 case WM_MOUSEWHEEL:
\r
4782 MouseEvent(hwnd, message, wParam, lParam);
\r
4786 if((char)wParam == '\b') {
\r
4787 ForwardEvent(); peek = 0;
\r
4790 JAWS_KBUP_NAVIGATION
\r
4795 if((char)wParam == '\b') {
\r
4796 if(!peek) BackwardEvent(), peek = 1;
\r
4799 JAWS_KBDOWN_NAVIGATION
\r
4805 JAWS_ALT_INTERCEPT
\r
4807 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4808 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4809 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4810 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4812 SendMessage(h, message, wParam, lParam);
\r
4813 } else if(lParam != KF_REPEAT) {
\r
4814 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4815 TypeInEvent((char)wParam);
\r
4816 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4817 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4822 case WM_PALETTECHANGED:
\r
4823 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4825 HDC hdc = GetDC(hwndMain);
\r
4826 SelectPalette(hdc, hPal, TRUE);
\r
4827 nnew = RealizePalette(hdc);
\r
4829 paletteChanged = TRUE;
\r
4831 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4833 ReleaseDC(hwnd, hdc);
\r
4837 case WM_QUERYNEWPALETTE:
\r
4838 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4840 HDC hdc = GetDC(hwndMain);
\r
4841 paletteChanged = FALSE;
\r
4842 SelectPalette(hdc, hPal, FALSE);
\r
4843 nnew = RealizePalette(hdc);
\r
4845 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4847 ReleaseDC(hwnd, hdc);
\r
4852 case WM_COMMAND: /* message: command from application menu */
\r
4853 wmId = LOWORD(wParam);
\r
4858 SAY("new game enter a move to play against the computer with white");
\r
4861 case IDM_NewGameFRC:
\r
4862 if( NewGameFRC() == 0 ) {
\r
4867 case IDM_NewVariant:
\r
4868 NewVariantPopup(hwnd);
\r
4871 case IDM_LoadGame:
\r
4872 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4875 case IDM_LoadNextGame:
\r
4879 case IDM_LoadPrevGame:
\r
4883 case IDM_ReloadGame:
\r
4887 case IDM_LoadPosition:
\r
4888 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4889 Reset(FALSE, TRUE);
\r
4892 f = OpenFileDialog(hwnd, "rb", "",
\r
4893 appData.oldSaveStyle ? "pos" : "fen",
\r
4895 _("Load Position from File"), &number, fileTitle, NULL);
\r
4897 LoadPosition(f, number, fileTitle);
\r
4901 case IDM_LoadNextPosition:
\r
4902 ReloadPosition(1);
\r
4905 case IDM_LoadPrevPosition:
\r
4906 ReloadPosition(-1);
\r
4909 case IDM_ReloadPosition:
\r
4910 ReloadPosition(0);
\r
4913 case IDM_SaveGame:
\r
4914 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4915 f = OpenFileDialog(hwnd, "a", defName,
\r
4916 appData.oldSaveStyle ? "gam" : "pgn",
\r
4918 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4920 SaveGame(f, 0, "");
\r
4924 case IDM_SavePosition:
\r
4925 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4926 f = OpenFileDialog(hwnd, "a", defName,
\r
4927 appData.oldSaveStyle ? "pos" : "fen",
\r
4929 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4931 SavePosition(f, 0, "");
\r
4935 case IDM_SaveDiagram:
\r
4936 defName = "diagram";
\r
4937 f = OpenFileDialog(hwnd, "wb", defName,
\r
4940 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
4946 case IDM_SaveSelected:
\r
4947 f = OpenFileDialog(hwnd, "a", "",
\r
4950 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4952 SaveSelected(f, 0, "");
\r
4956 case IDM_CreateBook:
\r
4957 CreateBookEvent();
\r
4960 case IDM_CopyGame:
\r
4961 CopyGameToClipboard();
\r
4964 case IDM_PasteGame:
\r
4965 PasteGameFromClipboard();
\r
4968 case IDM_CopyGameListToClipboard:
\r
4969 CopyGameListToClipboard();
\r
4972 /* [AS] Autodetect FEN or PGN data */
\r
4973 case IDM_PasteAny:
\r
4974 PasteGameOrFENFromClipboard();
\r
4977 /* [AS] Move history */
\r
4978 case IDM_ShowMoveHistory:
\r
4979 if( MoveHistoryIsUp() ) {
\r
4980 MoveHistoryPopDown();
\r
4983 MoveHistoryPopUp();
\r
4987 /* [AS] Eval graph */
\r
4988 case IDM_ShowEvalGraph:
\r
4989 if( EvalGraphIsUp() ) {
\r
4990 EvalGraphPopDown();
\r
4994 SetFocus(hwndMain);
\r
4998 /* [AS] Engine output */
\r
4999 case IDM_ShowEngineOutput:
\r
5000 if( EngineOutputIsUp() ) {
\r
5001 EngineOutputPopDown();
\r
5004 EngineOutputPopUp();
\r
5008 /* [AS] User adjudication */
\r
5009 case IDM_UserAdjudication_White:
\r
5010 UserAdjudicationEvent( +1 );
\r
5013 case IDM_UserAdjudication_Black:
\r
5014 UserAdjudicationEvent( -1 );
\r
5017 case IDM_UserAdjudication_Draw:
\r
5018 UserAdjudicationEvent( 0 );
\r
5021 /* [AS] Game list options dialog */
\r
5022 case IDM_GameListOptions:
\r
5023 GameListOptions();
\r
5030 case IDM_CopyPosition:
\r
5031 CopyFENToClipboard();
\r
5034 case IDM_PastePosition:
\r
5035 PasteFENFromClipboard();
\r
5038 case IDM_MailMove:
\r
5042 case IDM_ReloadCMailMsg:
\r
5043 Reset(TRUE, TRUE);
\r
5044 ReloadCmailMsgEvent(FALSE);
\r
5047 case IDM_Minimize:
\r
5048 ShowWindow(hwnd, SW_MINIMIZE);
\r
5055 case IDM_MachineWhite:
\r
5056 MachineWhiteEvent();
\r
5058 * refresh the tags dialog only if it's visible
\r
5060 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
5062 tags = PGNTags(&gameInfo);
\r
5063 TagsPopUp(tags, CmailMsg());
\r
5066 SAY("computer starts playing white");
\r
5069 case IDM_MachineBlack:
\r
5070 MachineBlackEvent();
\r
5072 * refresh the tags dialog only if it's visible
\r
5074 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
5076 tags = PGNTags(&gameInfo);
\r
5077 TagsPopUp(tags, CmailMsg());
\r
5080 SAY("computer starts playing black");
\r
5083 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
5084 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
5087 case IDM_TwoMachines:
\r
5088 TwoMachinesEvent();
\r
5090 * refresh the tags dialog only if it's visible
\r
5092 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
5094 tags = PGNTags(&gameInfo);
\r
5095 TagsPopUp(tags, CmailMsg());
\r
5098 SAY("computer starts playing both sides");
\r
5101 case IDM_AnalysisMode:
\r
5102 if(AnalyzeModeEvent()) {
\r
5103 SAY("analyzing current position");
\r
5107 case IDM_AnalyzeFile:
\r
5108 AnalyzeFileEvent();
\r
5111 case IDM_IcsClient:
\r
5115 case IDM_EditGame:
\r
5116 case IDM_EditGame2:
\r
5121 case IDM_EditPosition:
\r
5122 case IDM_EditPosition2:
\r
5123 EditPositionEvent();
\r
5124 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
5127 case IDM_Training:
\r
5131 case IDM_ShowGameList:
\r
5132 ShowGameListProc();
\r
5135 case IDM_EditProgs1:
\r
5136 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
5139 case IDM_LoadProg1:
\r
5140 LoadEnginePopUp(hwndMain, 0);
\r
5143 case IDM_LoadProg2:
\r
5144 LoadEnginePopUp(hwndMain, 1);
\r
5147 case IDM_EditServers:
\r
5148 EditTagsPopUp(icsNames, &icsNames);
\r
5151 case IDM_EditTags:
\r
5156 case IDM_EditBook:
\r
5160 case IDM_EditComment:
\r
5162 if (commentUp && editComment) {
\r
5165 EditCommentEvent();
\r
5186 case IDM_CallFlag:
\r
5206 case IDM_StopObserving:
\r
5207 StopObservingEvent();
\r
5210 case IDM_StopExamining:
\r
5211 StopExaminingEvent();
\r
5215 UploadGameEvent();
\r
5218 case IDM_TypeInMove:
\r
5219 TypeInEvent('\000');
\r
5222 case IDM_TypeInName:
\r
5223 PopUpNameDialog('\000');
\r
5226 case IDM_Backward:
\r
5228 SetFocus(hwndMain);
\r
5235 SetFocus(hwndMain);
\r
5240 SetFocus(hwndMain);
\r
5245 SetFocus(hwndMain);
\r
5248 case OPT_GameListNext: // [HGM] forward these two accelerators to Game List
\r
5249 case OPT_GameListPrev:
\r
5250 if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);
\r
5254 RevertEvent(FALSE);
\r
5257 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5258 RevertEvent(TRUE);
\r
5261 case IDM_TruncateGame:
\r
5262 TruncateGameEvent();
\r
5269 case IDM_RetractMove:
\r
5270 RetractMoveEvent();
\r
5273 case IDM_FlipView:
\r
5274 flipView = !flipView;
\r
5275 DrawPosition(FALSE, NULL);
\r
5278 case IDM_FlipClock:
\r
5279 flipClock = !flipClock;
\r
5280 DisplayBothClocks();
\r
5284 case IDM_MuteSounds:
\r
5285 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5286 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5287 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5290 case IDM_GeneralOptions:
\r
5291 GeneralOptionsPopup(hwnd);
\r
5292 DrawPosition(TRUE, NULL);
\r
5295 case IDM_BoardOptions:
\r
5296 BoardOptionsPopup(hwnd);
\r
5299 case IDM_ThemeOptions:
\r
5300 ThemeOptionsPopup(hwnd);
\r
5303 case IDM_EnginePlayOptions:
\r
5304 EnginePlayOptionsPopup(hwnd);
\r
5307 case IDM_Engine1Options:
\r
5308 EngineOptionsPopup(hwnd, &first);
\r
5311 case IDM_Engine2Options:
\r
5313 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5314 EngineOptionsPopup(hwnd, &second);
\r
5317 case IDM_OptionsUCI:
\r
5318 UciOptionsPopup(hwnd);
\r
5322 TourneyPopup(hwnd);
\r
5325 case IDM_IcsOptions:
\r
5326 IcsOptionsPopup(hwnd);
\r
5330 FontsOptionsPopup(hwnd);
\r
5334 SoundOptionsPopup(hwnd);
\r
5337 case IDM_CommPort:
\r
5338 CommPortOptionsPopup(hwnd);
\r
5341 case IDM_LoadOptions:
\r
5342 LoadOptionsPopup(hwnd);
\r
5345 case IDM_SaveOptions:
\r
5346 SaveOptionsPopup(hwnd);
\r
5349 case IDM_TimeControl:
\r
5350 TimeControlOptionsPopup(hwnd);
\r
5353 case IDM_SaveSettings:
\r
5354 SaveSettings(settingsFileName);
\r
5357 case IDM_SaveSettingsOnExit:
\r
5358 saveSettingsOnExit = !saveSettingsOnExit;
\r
5359 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5360 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5361 MF_CHECKED : MF_UNCHECKED));
\r
5372 case IDM_AboutGame:
\r
5377 appData.debugMode = !appData.debugMode;
\r
5378 if (appData.debugMode) {
\r
5379 char dir[MSG_SIZ];
\r
5380 GetCurrentDirectory(MSG_SIZ, dir);
\r
5381 SetCurrentDirectory(installDir);
\r
5382 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5383 SetCurrentDirectory(dir);
\r
5384 setbuf(debugFP, NULL);
\r
5391 case IDM_HELPCONTENTS:
\r
5392 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5393 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5394 MessageBox (GetFocus(),
\r
5395 _("Unable to activate help"),
\r
5396 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5400 case IDM_HELPSEARCH:
\r
5401 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5402 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5403 MessageBox (GetFocus(),
\r
5404 _("Unable to activate help"),
\r
5405 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5409 case IDM_HELPHELP:
\r
5410 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5411 MessageBox (GetFocus(),
\r
5412 _("Unable to activate help"),
\r
5413 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5418 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5420 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5421 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5422 FreeProcInstance(lpProc);
\r
5425 case IDM_DirectCommand1:
\r
5426 AskQuestionEvent(_("Direct Command"),
\r
5427 _("Send to chess program:"), "", "1");
\r
5429 case IDM_DirectCommand2:
\r
5430 AskQuestionEvent(_("Direct Command"),
\r
5431 _("Send to second chess program:"), "", "2");
\r
5434 case EP_WhitePawn:
\r
5435 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5436 fromX = fromY = -1;
\r
5439 case EP_WhiteKnight:
\r
5440 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5441 fromX = fromY = -1;
\r
5444 case EP_WhiteBishop:
\r
5445 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5446 fromX = fromY = -1;
\r
5449 case EP_WhiteRook:
\r
5450 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5451 fromX = fromY = -1;
\r
5454 case EP_WhiteQueen:
\r
5455 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5456 fromX = fromY = -1;
\r
5459 case EP_WhiteFerz:
\r
5460 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5461 fromX = fromY = -1;
\r
5464 case EP_WhiteWazir:
\r
5465 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5466 fromX = fromY = -1;
\r
5469 case EP_WhiteAlfil:
\r
5470 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5471 fromX = fromY = -1;
\r
5474 case EP_WhiteCannon:
\r
5475 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5476 fromX = fromY = -1;
\r
5479 case EP_WhiteCardinal:
\r
5480 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5481 fromX = fromY = -1;
\r
5484 case EP_WhiteMarshall:
\r
5485 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5486 fromX = fromY = -1;
\r
5489 case EP_WhiteKing:
\r
5490 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5491 fromX = fromY = -1;
\r
5494 case EP_BlackPawn:
\r
5495 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5496 fromX = fromY = -1;
\r
5499 case EP_BlackKnight:
\r
5500 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5501 fromX = fromY = -1;
\r
5504 case EP_BlackBishop:
\r
5505 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5506 fromX = fromY = -1;
\r
5509 case EP_BlackRook:
\r
5510 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5511 fromX = fromY = -1;
\r
5514 case EP_BlackQueen:
\r
5515 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5516 fromX = fromY = -1;
\r
5519 case EP_BlackFerz:
\r
5520 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5521 fromX = fromY = -1;
\r
5524 case EP_BlackWazir:
\r
5525 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5526 fromX = fromY = -1;
\r
5529 case EP_BlackAlfil:
\r
5530 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5531 fromX = fromY = -1;
\r
5534 case EP_BlackCannon:
\r
5535 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5536 fromX = fromY = -1;
\r
5539 case EP_BlackCardinal:
\r
5540 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5541 fromX = fromY = -1;
\r
5544 case EP_BlackMarshall:
\r
5545 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5546 fromX = fromY = -1;
\r
5549 case EP_BlackKing:
\r
5550 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5551 fromX = fromY = -1;
\r
5554 case EP_EmptySquare:
\r
5555 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5556 fromX = fromY = -1;
\r
5559 case EP_ClearBoard:
\r
5560 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5561 fromX = fromY = -1;
\r
5565 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5566 fromX = fromY = -1;
\r
5570 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5571 fromX = fromY = -1;
\r
5575 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5576 fromX = fromY = -1;
\r
5580 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5581 fromX = fromY = -1;
\r
5585 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5586 fromX = fromY = -1;
\r
5590 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5591 fromX = fromY = -1;
\r
5595 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5596 fromX = fromY = -1;
\r
5600 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5601 fromX = fromY = -1;
\r
5605 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5606 fromX = fromY = -1;
\r
5610 barbaric = 0; appData.language = "";
\r
5611 TranslateMenus(0);
\r
5612 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5613 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5614 lastChecked = wmId;
\r
5618 if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)
\r
5619 RecentEngineEvent(wmId - IDM_RecentEngines);
\r
5621 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5622 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5623 TranslateMenus(0);
\r
5624 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5625 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5626 lastChecked = wmId;
\r
5629 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5635 case CLOCK_TIMER_ID:
\r
5636 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5637 clockTimerEvent = 0;
\r
5638 DecrementClocks(); /* call into back end */
\r
5640 case LOAD_GAME_TIMER_ID:
\r
5641 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5642 loadGameTimerEvent = 0;
\r
5643 AutoPlayGameLoop(); /* call into back end */
\r
5645 case ANALYSIS_TIMER_ID:
\r
5646 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5647 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5648 AnalysisPeriodicEvent(0);
\r
5650 KillTimer(hwnd, analysisTimerEvent);
\r
5651 analysisTimerEvent = 0;
\r
5654 case DELAYED_TIMER_ID:
\r
5655 KillTimer(hwnd, delayedTimerEvent);
\r
5656 delayedTimerEvent = 0;
\r
5657 delayedTimerCallback();
\r
5662 case WM_USER_Input:
\r
5663 InputEvent(hwnd, message, wParam, lParam);
\r
5666 /* [AS] Also move "attached" child windows */
\r
5667 case WM_WINDOWPOSCHANGING:
\r
5669 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5670 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5672 if( ((lpwp->flags & SWP_NOMOVE) == 0) /*&& ((lpwp->flags & SWP_NOSIZE) != 0)*/ ) { // [HGM] in Win8 size always accompanies move?
\r
5673 /* Window is moving */
\r
5676 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5677 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5678 rcMain.right = wpMain.x + wpMain.width;
\r
5679 rcMain.top = wpMain.y;
\r
5680 rcMain.bottom = wpMain.y + wpMain.height;
\r
5682 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5683 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5684 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5685 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5686 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5687 wpMain.x = lpwp->x;
\r
5688 wpMain.y = lpwp->y;
\r
5693 /* [AS] Snapping */
\r
5694 case WM_ENTERSIZEMOVE:
\r
5695 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5696 if (hwnd == hwndMain) {
\r
5697 doingSizing = TRUE;
\r
5700 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5704 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5705 if (hwnd == hwndMain) {
\r
5706 lastSizing = wParam;
\r
5711 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5712 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5714 case WM_EXITSIZEMOVE:
\r
5715 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5716 if (hwnd == hwndMain) {
\r
5718 doingSizing = FALSE;
\r
5719 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5720 GetClientRect(hwnd, &client);
\r
5721 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5723 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5725 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5728 case WM_DESTROY: /* message: window being destroyed */
\r
5729 PostQuitMessage(0);
\r
5733 if (hwnd == hwndMain) {
\r
5738 default: /* Passes it on if unprocessed */
\r
5739 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5745 /*---------------------------------------------------------------------------*\
\r
5747 * Misc utility routines
\r
5749 \*---------------------------------------------------------------------------*/
\r
5752 * Decent random number generator, at least not as bad as Windows
\r
5753 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5755 unsigned int randstate;
\r
5760 randstate = randstate * 1664525 + 1013904223;
\r
5761 return (int) randstate & 0x7fffffff;
\r
5765 mysrandom(unsigned int seed)
\r
5772 * returns TRUE if user selects a different color, FALSE otherwise
\r
5776 ChangeColor(HWND hwnd, COLORREF *which)
\r
5778 static BOOL firstTime = TRUE;
\r
5779 static DWORD customColors[16];
\r
5781 COLORREF newcolor;
\r
5786 /* Make initial colors in use available as custom colors */
\r
5787 /* Should we put the compiled-in defaults here instead? */
\r
5789 customColors[i++] = lightSquareColor & 0xffffff;
\r
5790 customColors[i++] = darkSquareColor & 0xffffff;
\r
5791 customColors[i++] = whitePieceColor & 0xffffff;
\r
5792 customColors[i++] = blackPieceColor & 0xffffff;
\r
5793 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5794 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5796 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5797 customColors[i++] = textAttribs[ccl].color;
\r
5799 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5800 firstTime = FALSE;
\r
5803 cc.lStructSize = sizeof(cc);
\r
5804 cc.hwndOwner = hwnd;
\r
5805 cc.hInstance = NULL;
\r
5806 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5807 cc.lpCustColors = (LPDWORD) customColors;
\r
5808 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5810 if (!ChooseColor(&cc)) return FALSE;
\r
5812 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5813 if (newcolor == *which) return FALSE;
\r
5814 *which = newcolor;
\r
5818 InitDrawingColors();
\r
5819 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5824 MyLoadSound(MySound *ms)
\r
5830 if (ms->data && ms->flag) free(ms->data);
\r
5833 switch (ms->name[0]) {
\r
5839 /* System sound from Control Panel. Don't preload here. */
\r
5843 if (ms->name[1] == NULLCHAR) {
\r
5844 /* "!" alone = silence */
\r
5847 /* Builtin wave resource. Error if not found. */
\r
5848 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5849 if (h == NULL) break;
\r
5850 ms->data = (void *)LoadResource(hInst, h);
\r
5851 ms->flag = 0; // not maloced, so cannot be freed!
\r
5852 if (h == NULL) break;
\r
5857 /* .wav file. Error if not found. */
\r
5858 f = fopen(ms->name, "rb");
\r
5859 if (f == NULL) break;
\r
5860 if (fstat(fileno(f), &st) < 0) break;
\r
5861 ms->data = malloc(st.st_size);
\r
5863 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5869 char buf[MSG_SIZ];
\r
5870 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5871 DisplayError(buf, GetLastError());
\r
5877 MyPlaySound(MySound *ms)
\r
5879 BOOLEAN ok = FALSE;
\r
5881 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5882 switch (ms->name[0]) {
\r
5884 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5889 /* System sound from Control Panel (deprecated feature).
\r
5890 "$" alone or an unset sound name gets default beep (still in use). */
\r
5891 if (ms->name[1]) {
\r
5892 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5894 if (!ok) ok = MessageBeep(MB_OK);
\r
5897 /* Builtin wave resource, or "!" alone for silence */
\r
5898 if (ms->name[1]) {
\r
5899 if (ms->data == NULL) return FALSE;
\r
5900 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5906 /* .wav file. Error if not found. */
\r
5907 if (ms->data == NULL) return FALSE;
\r
5908 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5911 /* Don't print an error: this can happen innocently if the sound driver
\r
5912 is busy; for instance, if another instance of WinBoard is playing
\r
5913 a sound at about the same time. */
\r
5919 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5922 OPENFILENAME *ofn;
\r
5923 static UINT *number; /* gross that this is static */
\r
5925 switch (message) {
\r
5926 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5927 /* Center the dialog over the application window */
\r
5928 ofn = (OPENFILENAME *) lParam;
\r
5929 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5930 number = (UINT *) ofn->lCustData;
\r
5931 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5935 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5936 Translate(hDlg, 1536);
\r
5937 return FALSE; /* Allow for further processing */
\r
5940 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5941 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5943 return FALSE; /* Allow for further processing */
\r
5949 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5951 static UINT *number;
\r
5952 OPENFILENAME *ofname;
\r
5955 case WM_INITDIALOG:
\r
5956 Translate(hdlg, DLG_IndexNumber);
\r
5957 ofname = (OPENFILENAME *)lParam;
\r
5958 number = (UINT *)(ofname->lCustData);
\r
5961 ofnot = (OFNOTIFY *)lParam;
\r
5962 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5963 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5972 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5973 char *nameFilt, char *dlgTitle, UINT *number,
\r
5974 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5976 OPENFILENAME openFileName;
\r
5977 char buf1[MSG_SIZ];
\r
5980 if (fileName == NULL) fileName = buf1;
\r
5981 if (defName == NULL) {
\r
5982 safeStrCpy(fileName, "*.", 3 );
\r
5983 strcat(fileName, defExt);
\r
5985 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5987 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5988 if (number) *number = 0;
\r
5990 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5991 openFileName.hwndOwner = hwnd;
\r
5992 openFileName.hInstance = (HANDLE) hInst;
\r
5993 openFileName.lpstrFilter = nameFilt;
\r
5994 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5995 openFileName.nMaxCustFilter = 0L;
\r
5996 openFileName.nFilterIndex = 1L;
\r
5997 openFileName.lpstrFile = fileName;
\r
5998 openFileName.nMaxFile = MSG_SIZ;
\r
5999 openFileName.lpstrFileTitle = fileTitle;
\r
6000 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
6001 openFileName.lpstrInitialDir = NULL;
\r
6002 openFileName.lpstrTitle = dlgTitle;
\r
6003 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
6004 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
6005 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
6006 | (oldDialog ? 0 : OFN_EXPLORER);
\r
6007 openFileName.nFileOffset = 0;
\r
6008 openFileName.nFileExtension = 0;
\r
6009 openFileName.lpstrDefExt = defExt;
\r
6010 openFileName.lCustData = (LONG) number;
\r
6011 openFileName.lpfnHook = oldDialog ?
\r
6012 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
6013 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
6015 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
6016 GetOpenFileName(&openFileName)) {
\r
6017 /* open the file */
\r
6018 f = fopen(openFileName.lpstrFile, write);
\r
6020 MessageBox(hwnd, _("File open failed"), NULL,
\r
6021 MB_OK|MB_ICONEXCLAMATION);
\r
6025 int err = CommDlgExtendedError();
\r
6026 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
6035 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
6037 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
6040 * Get the first pop-up menu in the menu template. This is the
\r
6041 * menu that TrackPopupMenu displays.
\r
6043 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
6044 TranslateOneMenu(10, hmenuTrackPopup);
\r
6046 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
6049 * TrackPopup uses screen coordinates, so convert the
\r
6050 * coordinates of the mouse click to screen coordinates.
\r
6052 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
6054 /* Draw and track the floating pop-up menu. */
\r
6055 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
6056 pt.x, pt.y, 0, hwnd, NULL);
\r
6058 /* Destroy the menu.*/
\r
6059 DestroyMenu(hmenu);
\r
6064 int sizeX, sizeY, newSizeX, newSizeY;
\r
6066 } ResizeEditPlusButtonsClosure;
\r
6069 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
6071 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
6075 if (hChild == cl->hText) return TRUE;
\r
6076 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
6077 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
6078 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
6079 ScreenToClient(cl->hDlg, &pt);
\r
6080 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
6081 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
6085 /* Resize a dialog that has a (rich) edit field filling most of
\r
6086 the top, with a row of buttons below */
\r
6088 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
6091 int newTextHeight, newTextWidth;
\r
6092 ResizeEditPlusButtonsClosure cl;
\r
6094 /*if (IsIconic(hDlg)) return;*/
\r
6095 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
6097 cl.hdwp = BeginDeferWindowPos(8);
\r
6099 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
6100 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6101 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6102 if (newTextHeight < 0) {
\r
6103 newSizeY += -newTextHeight;
\r
6104 newTextHeight = 0;
\r
6106 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
6107 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6113 cl.newSizeX = newSizeX;
\r
6114 cl.newSizeY = newSizeY;
\r
6115 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
6117 EndDeferWindowPos(cl.hdwp);
\r
6120 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
6122 RECT rChild, rParent;
\r
6123 int wChild, hChild, wParent, hParent;
\r
6124 int wScreen, hScreen, xNew, yNew;
\r
6127 /* Get the Height and Width of the child window */
\r
6128 GetWindowRect (hwndChild, &rChild);
\r
6129 wChild = rChild.right - rChild.left;
\r
6130 hChild = rChild.bottom - rChild.top;
\r
6132 /* Get the Height and Width of the parent window */
\r
6133 GetWindowRect (hwndParent, &rParent);
\r
6134 wParent = rParent.right - rParent.left;
\r
6135 hParent = rParent.bottom - rParent.top;
\r
6137 /* Get the display limits */
\r
6138 hdc = GetDC (hwndChild);
\r
6139 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
6140 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
6141 ReleaseDC(hwndChild, hdc);
\r
6143 /* Calculate new X position, then adjust for screen */
\r
6144 xNew = rParent.left + ((wParent - wChild) /2);
\r
6147 } else if ((xNew+wChild) > wScreen) {
\r
6148 xNew = wScreen - wChild;
\r
6151 /* Calculate new Y position, then adjust for screen */
\r
6153 yNew = rParent.top + ((hParent - hChild) /2);
\r
6156 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
6161 } else if ((yNew+hChild) > hScreen) {
\r
6162 yNew = hScreen - hChild;
\r
6165 /* Set it, and return */
\r
6166 return SetWindowPos (hwndChild, NULL,
\r
6167 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
6170 /* Center one window over another */
\r
6171 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
6173 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
6176 /*---------------------------------------------------------------------------*\
\r
6178 * Startup Dialog functions
\r
6180 \*---------------------------------------------------------------------------*/
\r
6182 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
6184 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6186 while (*cd != NULL) {
\r
6187 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
6193 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
6195 char buf1[MAX_ARG_LEN];
\r
6198 if (str[0] == '@') {
\r
6199 FILE* f = fopen(str + 1, "r");
\r
6201 DisplayFatalError(str + 1, errno, 2);
\r
6204 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
6206 buf1[len] = NULLCHAR;
\r
6210 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6213 char buf[MSG_SIZ];
\r
6214 char *end = strchr(str, '\n');
\r
6215 if (end == NULL) return;
\r
6216 memcpy(buf, str, end - str);
\r
6217 buf[end - str] = NULLCHAR;
\r
6218 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6224 SetStartupDialogEnables(HWND hDlg)
\r
6226 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6227 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6228 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6229 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6230 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6231 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6232 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6233 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6234 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6235 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6236 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6237 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6238 IsDlgButtonChecked(hDlg, OPT_View));
\r
6242 QuoteForFilename(char *filename)
\r
6244 int dquote, space;
\r
6245 dquote = strchr(filename, '"') != NULL;
\r
6246 space = strchr(filename, ' ') != NULL;
\r
6247 if (dquote || space) {
\r
6259 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6261 char buf[MSG_SIZ];
\r
6264 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6265 q = QuoteForFilename(nthcp);
\r
6266 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6267 if (*nthdir != NULLCHAR) {
\r
6268 q = QuoteForFilename(nthdir);
\r
6269 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6271 if (*nthcp == NULLCHAR) {
\r
6272 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6273 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6274 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6275 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6280 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6282 char buf[MSG_SIZ];
\r
6286 switch (message) {
\r
6287 case WM_INITDIALOG:
\r
6288 /* Center the dialog */
\r
6289 CenterWindow (hDlg, GetDesktopWindow());
\r
6290 Translate(hDlg, DLG_Startup);
\r
6291 /* Initialize the dialog items */
\r
6292 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6293 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6294 firstChessProgramNames);
\r
6295 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6296 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6297 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6298 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6299 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6300 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6301 if (*appData.icsHelper != NULLCHAR) {
\r
6302 char *q = QuoteForFilename(appData.icsHelper);
\r
6303 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6305 if (*appData.icsHost == NULLCHAR) {
\r
6306 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6307 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6308 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6309 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6310 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6313 if (appData.icsActive) {
\r
6314 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6316 else if (appData.noChessProgram) {
\r
6317 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6320 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6323 SetStartupDialogEnables(hDlg);
\r
6327 switch (LOWORD(wParam)) {
\r
6329 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6330 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6331 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6333 comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox
\r
6334 ParseArgs(StringGet, &p);
\r
6335 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6336 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6338 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6339 ParseArgs(StringGet, &p);
\r
6340 SwapEngines(singleList); // ... and then make it 'second'
\r
6342 appData.noChessProgram = FALSE;
\r
6343 appData.icsActive = FALSE;
\r
6344 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6345 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6346 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6348 ParseArgs(StringGet, &p);
\r
6349 if (appData.zippyPlay) {
\r
6350 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6351 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6353 ParseArgs(StringGet, &p);
\r
6355 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6356 appData.noChessProgram = TRUE;
\r
6357 appData.icsActive = FALSE;
\r
6359 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6360 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6363 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6364 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6366 ParseArgs(StringGet, &p);
\r
6368 EndDialog(hDlg, TRUE);
\r
6375 case IDM_HELPCONTENTS:
\r
6376 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6377 MessageBox (GetFocus(),
\r
6378 _("Unable to activate help"),
\r
6379 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6384 SetStartupDialogEnables(hDlg);
\r
6392 /*---------------------------------------------------------------------------*\
\r
6394 * About box dialog functions
\r
6396 \*---------------------------------------------------------------------------*/
\r
6398 /* Process messages for "About" dialog box */
\r
6400 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6402 switch (message) {
\r
6403 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6404 /* Center the dialog over the application window */
\r
6405 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6406 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6407 Translate(hDlg, ABOUTBOX);
\r
6411 case WM_COMMAND: /* message: received a command */
\r
6412 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6413 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6414 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6422 /*---------------------------------------------------------------------------*\
\r
6424 * Comment Dialog functions
\r
6426 \*---------------------------------------------------------------------------*/
\r
6429 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6431 static HANDLE hwndText = NULL;
\r
6432 int len, newSizeX, newSizeY;
\r
6433 static int sizeX, sizeY;
\r
6438 switch (message) {
\r
6439 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6440 /* Initialize the dialog items */
\r
6441 Translate(hDlg, DLG_EditComment);
\r
6442 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6443 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6444 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6445 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6446 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6447 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6448 SetWindowText(hDlg, commentTitle);
\r
6449 if (editComment) {
\r
6450 SetFocus(hwndText);
\r
6452 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6454 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6455 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6456 MAKELPARAM(FALSE, 0));
\r
6457 /* Size and position the dialog */
\r
6458 if (!commentDialog) {
\r
6459 commentDialog = hDlg;
\r
6460 GetClientRect(hDlg, &rect);
\r
6461 sizeX = rect.right;
\r
6462 sizeY = rect.bottom;
\r
6463 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6464 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6465 WINDOWPLACEMENT wp;
\r
6466 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6467 wp.length = sizeof(WINDOWPLACEMENT);
\r
6469 wp.showCmd = SW_SHOW;
\r
6470 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6471 wp.rcNormalPosition.left = wpComment.x;
\r
6472 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6473 wp.rcNormalPosition.top = wpComment.y;
\r
6474 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6475 SetWindowPlacement(hDlg, &wp);
\r
6477 GetClientRect(hDlg, &rect);
\r
6478 newSizeX = rect.right;
\r
6479 newSizeY = rect.bottom;
\r
6480 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6481 newSizeX, newSizeY);
\r
6486 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6489 case WM_COMMAND: /* message: received a command */
\r
6490 switch (LOWORD(wParam)) {
\r
6492 if (editComment) {
\r
6494 /* Read changed options from the dialog box */
\r
6495 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6496 len = GetWindowTextLength(hwndText);
\r
6497 str = (char *) malloc(len + 1);
\r
6498 GetWindowText(hwndText, str, len + 1);
\r
6507 ReplaceComment(commentIndex, str);
\r
6514 case OPT_CancelComment:
\r
6518 case OPT_ClearComment:
\r
6519 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6522 case OPT_EditComment:
\r
6523 EditCommentEvent();
\r
6531 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6532 if( wParam == OPT_CommentText ) {
\r
6533 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6535 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6536 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6540 pt.x = LOWORD( lpMF->lParam );
\r
6541 pt.y = HIWORD( lpMF->lParam );
\r
6543 if(lpMF->msg == WM_CHAR) {
\r
6545 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6546 index = sel.cpMin;
\r
6548 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6550 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6551 len = GetWindowTextLength(hwndText);
\r
6552 str = (char *) malloc(len + 1);
\r
6553 GetWindowText(hwndText, str, len + 1);
\r
6554 ReplaceComment(commentIndex, str);
\r
6555 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6556 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6559 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6560 lpMF->msg = WM_USER;
\r
6568 newSizeX = LOWORD(lParam);
\r
6569 newSizeY = HIWORD(lParam);
\r
6570 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6575 case WM_GETMINMAXINFO:
\r
6576 /* Prevent resizing window too small */
\r
6577 mmi = (MINMAXINFO *) lParam;
\r
6578 mmi->ptMinTrackSize.x = 100;
\r
6579 mmi->ptMinTrackSize.y = 100;
\r
6586 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6591 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6593 if (str == NULL) str = "";
\r
6594 p = (char *) malloc(2 * strlen(str) + 2);
\r
6597 if (*str == '\n') *q++ = '\r';
\r
6601 if (commentText != NULL) free(commentText);
\r
6603 commentIndex = index;
\r
6604 commentTitle = title;
\r
6606 editComment = edit;
\r
6608 if (commentDialog) {
\r
6609 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6610 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6612 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6613 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6614 hwndMain, (DLGPROC)lpProc);
\r
6615 FreeProcInstance(lpProc);
\r
6621 /*---------------------------------------------------------------------------*\
\r
6623 * Type-in move dialog functions
\r
6625 \*---------------------------------------------------------------------------*/
\r
6628 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6630 char move[MSG_SIZ];
\r
6633 switch (message) {
\r
6634 case WM_INITDIALOG:
\r
6635 move[0] = (char) lParam;
\r
6636 move[1] = NULLCHAR;
\r
6637 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6638 Translate(hDlg, DLG_TypeInMove);
\r
6639 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6640 SetWindowText(hInput, move);
\r
6642 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6646 switch (LOWORD(wParam)) {
\r
6649 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6650 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6651 TypeInDoneEvent(move);
\r
6652 EndDialog(hDlg, TRUE);
\r
6655 EndDialog(hDlg, FALSE);
\r
6666 PopUpMoveDialog(char firstchar)
\r
6670 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6671 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6672 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6673 FreeProcInstance(lpProc);
\r
6676 /*---------------------------------------------------------------------------*\
\r
6678 * Type-in name dialog functions
\r
6680 \*---------------------------------------------------------------------------*/
\r
6683 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6685 char move[MSG_SIZ];
\r
6688 switch (message) {
\r
6689 case WM_INITDIALOG:
\r
6690 move[0] = (char) lParam;
\r
6691 move[1] = NULLCHAR;
\r
6692 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6693 Translate(hDlg, DLG_TypeInName);
\r
6694 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6695 SetWindowText(hInput, move);
\r
6697 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6701 switch (LOWORD(wParam)) {
\r
6703 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6704 appData.userName = strdup(move);
\r
6707 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6708 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6709 DisplayTitle(move);
\r
6713 EndDialog(hDlg, TRUE);
\r
6716 EndDialog(hDlg, FALSE);
\r
6727 PopUpNameDialog(char firstchar)
\r
6731 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6732 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6733 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6734 FreeProcInstance(lpProc);
\r
6737 /*---------------------------------------------------------------------------*\
\r
6741 \*---------------------------------------------------------------------------*/
\r
6743 /* Nonmodal error box */
\r
6744 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6745 WPARAM wParam, LPARAM lParam);
\r
6748 ErrorPopUp(char *title, char *content)
\r
6752 BOOLEAN modal = hwndMain == NULL;
\r
6770 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6771 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6774 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6776 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6777 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6778 hwndMain, (DLGPROC)lpProc);
\r
6779 FreeProcInstance(lpProc);
\r
6786 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6787 if (errorDialog == NULL) return;
\r
6788 DestroyWindow(errorDialog);
\r
6789 errorDialog = NULL;
\r
6790 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6794 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6798 switch (message) {
\r
6799 case WM_INITDIALOG:
\r
6800 GetWindowRect(hDlg, &rChild);
\r
6803 SetWindowPos(hDlg, NULL, rChild.left,
\r
6804 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6805 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6809 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6810 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6811 and it doesn't work when you resize the dialog.
\r
6812 For now, just give it a default position.
\r
6814 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6815 Translate(hDlg, DLG_Error);
\r
6817 errorDialog = hDlg;
\r
6818 SetWindowText(hDlg, errorTitle);
\r
6819 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6823 switch (LOWORD(wParam)) {
\r
6826 if (errorDialog == hDlg) errorDialog = NULL;
\r
6827 DestroyWindow(hDlg);
\r
6839 HWND gothicDialog = NULL;
\r
6842 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6845 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6847 switch (message) {
\r
6848 case WM_INITDIALOG:
\r
6849 GetWindowRect(hDlg, &rChild);
\r
6851 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6855 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6856 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6857 and it doesn't work when you resize the dialog.
\r
6858 For now, just give it a default position.
\r
6860 gothicDialog = hDlg;
\r
6861 SetWindowText(hDlg, errorTitle);
\r
6862 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6866 switch (LOWORD(wParam)) {
\r
6869 if (errorDialog == hDlg) errorDialog = NULL;
\r
6870 DestroyWindow(hDlg);
\r
6882 GothicPopUp(char *title, VariantClass variant)
\r
6885 static char *lastTitle;
\r
6887 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6888 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6890 if(lastTitle != title && gothicDialog != NULL) {
\r
6891 DestroyWindow(gothicDialog);
\r
6892 gothicDialog = NULL;
\r
6894 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6895 title = lastTitle;
\r
6896 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6897 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6898 hwndMain, (DLGPROC)lpProc);
\r
6899 FreeProcInstance(lpProc);
\r
6904 /*---------------------------------------------------------------------------*\
\r
6906 * Ics Interaction console functions
\r
6908 \*---------------------------------------------------------------------------*/
\r
6910 #define HISTORY_SIZE 64
\r
6911 static char *history[HISTORY_SIZE];
\r
6912 int histIn = 0, histP = 0;
\r
6916 SaveInHistory(char *cmd)
\r
6918 if (history[histIn] != NULL) {
\r
6919 free(history[histIn]);
\r
6920 history[histIn] = NULL;
\r
6922 if (*cmd == NULLCHAR) return;
\r
6923 history[histIn] = StrSave(cmd);
\r
6924 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6925 if (history[histIn] != NULL) {
\r
6926 free(history[histIn]);
\r
6928 history[histIn] = NULL;
\r
6934 PrevInHistory(char *cmd)
\r
6937 if (histP == histIn) {
\r
6938 if (history[histIn] != NULL) free(history[histIn]);
\r
6939 history[histIn] = StrSave(cmd);
\r
6941 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6942 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6944 return history[histP];
\r
6950 if (histP == histIn) return NULL;
\r
6951 histP = (histP + 1) % HISTORY_SIZE;
\r
6952 return history[histP];
\r
6956 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6960 hmenu = LoadMenu(hInst, "TextMenu");
\r
6961 h = GetSubMenu(hmenu, 0);
\r
6963 if (strcmp(e->item, "-") == 0) {
\r
6964 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6965 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6966 int flags = MF_STRING, j = 0;
\r
6967 if (e->item[0] == '|') {
\r
6968 flags |= MF_MENUBARBREAK;
\r
6971 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6972 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6980 WNDPROC consoleTextWindowProc;
\r
6983 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6985 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6986 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6990 SetWindowText(hInput, command);
\r
6992 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6994 sel.cpMin = 999999;
\r
6995 sel.cpMax = 999999;
\r
6996 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7001 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7002 if (sel.cpMin == sel.cpMax) {
\r
7003 /* Expand to surrounding word */
\r
7006 tr.chrg.cpMax = sel.cpMin;
\r
7007 tr.chrg.cpMin = --sel.cpMin;
\r
7008 if (sel.cpMin < 0) break;
\r
7009 tr.lpstrText = name;
\r
7010 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
7011 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
7015 tr.chrg.cpMin = sel.cpMax;
\r
7016 tr.chrg.cpMax = ++sel.cpMax;
\r
7017 tr.lpstrText = name;
\r
7018 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
7019 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
7022 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
7023 MessageBeep(MB_ICONEXCLAMATION);
\r
7027 tr.lpstrText = name;
\r
7028 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
7030 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
7031 MessageBeep(MB_ICONEXCLAMATION);
\r
7034 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
7037 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
7038 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
7039 SetWindowText(hInput, buf);
\r
7040 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
7042 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
7043 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
7044 SetWindowText(hInput, buf);
\r
7045 sel.cpMin = 999999;
\r
7046 sel.cpMax = 999999;
\r
7047 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7053 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7058 switch (message) {
\r
7060 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7061 if(wParam=='R') return 0;
\r
7064 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
7067 sel.cpMin = 999999;
\r
7068 sel.cpMax = 999999;
\r
7069 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7070 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
7075 if(wParam != '\022') {
\r
7076 if (wParam == '\t') {
\r
7077 if (GetKeyState(VK_SHIFT) < 0) {
\r
7079 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7080 if (buttonDesc[0].hwnd) {
\r
7081 SetFocus(buttonDesc[0].hwnd);
\r
7083 SetFocus(hwndMain);
\r
7087 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
7090 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7091 JAWS_DELETE( SetFocus(hInput); )
\r
7092 SendMessage(hInput, message, wParam, lParam);
\r
7095 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
7097 case WM_RBUTTONDOWN:
\r
7098 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
7099 /* Move selection here if it was empty */
\r
7101 pt.x = LOWORD(lParam);
\r
7102 pt.y = HIWORD(lParam);
\r
7103 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7104 if (sel.cpMin == sel.cpMax) {
\r
7105 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
7106 sel.cpMax = sel.cpMin;
\r
7107 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7109 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
7110 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
7112 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
7113 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7114 if (sel.cpMin == sel.cpMax) {
\r
7115 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7116 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
7118 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7119 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7121 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
7122 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
7123 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
7124 MenuPopup(hwnd, pt, hmenu, -1);
\r
7128 case WM_RBUTTONUP:
\r
7129 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7130 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7131 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7135 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7137 return SendMessage(hInput, message, wParam, lParam);
\r
7138 case WM_MBUTTONDOWN:
\r
7139 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7141 switch (LOWORD(wParam)) {
\r
7142 case IDM_QuickPaste:
\r
7144 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7145 if (sel.cpMin == sel.cpMax) {
\r
7146 MessageBeep(MB_ICONEXCLAMATION);
\r
7149 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7150 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7151 SendMessage(hInput, WM_PASTE, 0, 0);
\r
7156 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7159 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7162 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7166 int i = LOWORD(wParam) - IDM_CommandX;
\r
7167 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
7168 icsTextMenuEntry[i].command != NULL) {
\r
7169 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
7170 icsTextMenuEntry[i].getname,
\r
7171 icsTextMenuEntry[i].immediate);
\r
7179 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
7182 WNDPROC consoleInputWindowProc;
\r
7185 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7187 char buf[MSG_SIZ];
\r
7189 static BOOL sendNextChar = FALSE;
\r
7190 static BOOL quoteNextChar = FALSE;
\r
7191 InputSource *is = consoleInputSource;
\r
7195 switch (message) {
\r
7197 if (!appData.localLineEditing || sendNextChar) {
\r
7198 is->buf[0] = (CHAR) wParam;
\r
7200 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7201 sendNextChar = FALSE;
\r
7204 if (quoteNextChar) {
\r
7205 buf[0] = (char) wParam;
\r
7206 buf[1] = NULLCHAR;
\r
7207 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7208 quoteNextChar = FALSE;
\r
7212 case '\r': /* Enter key */
\r
7213 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7214 if (consoleEcho) SaveInHistory(is->buf);
\r
7215 is->buf[is->count++] = '\n';
\r
7216 is->buf[is->count] = NULLCHAR;
\r
7217 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7218 if (consoleEcho) {
\r
7219 ConsoleOutput(is->buf, is->count, TRUE);
\r
7220 } else if (appData.localLineEditing) {
\r
7221 ConsoleOutput("\n", 1, TRUE);
\r
7224 case '\033': /* Escape key */
\r
7225 SetWindowText(hwnd, "");
\r
7226 cf.cbSize = sizeof(CHARFORMAT);
\r
7227 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7228 if (consoleEcho) {
\r
7229 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7231 cf.crTextColor = COLOR_ECHOOFF;
\r
7233 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7234 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7236 case '\t': /* Tab key */
\r
7237 if (GetKeyState(VK_SHIFT) < 0) {
\r
7239 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7242 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7243 if (buttonDesc[0].hwnd) {
\r
7244 SetFocus(buttonDesc[0].hwnd);
\r
7246 SetFocus(hwndMain);
\r
7250 case '\023': /* Ctrl+S */
\r
7251 sendNextChar = TRUE;
\r
7253 case '\021': /* Ctrl+Q */
\r
7254 quoteNextChar = TRUE;
\r
7264 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7265 p = PrevInHistory(buf);
\r
7267 SetWindowText(hwnd, p);
\r
7268 sel.cpMin = 999999;
\r
7269 sel.cpMax = 999999;
\r
7270 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7275 p = NextInHistory();
\r
7277 SetWindowText(hwnd, p);
\r
7278 sel.cpMin = 999999;
\r
7279 sel.cpMax = 999999;
\r
7280 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7286 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7290 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7294 case WM_MBUTTONDOWN:
\r
7295 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7296 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7298 case WM_RBUTTONUP:
\r
7299 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7300 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7301 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7305 hmenu = LoadMenu(hInst, "InputMenu");
\r
7306 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7307 if (sel.cpMin == sel.cpMax) {
\r
7308 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7309 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7311 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7312 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7314 pt.x = LOWORD(lParam);
\r
7315 pt.y = HIWORD(lParam);
\r
7316 MenuPopup(hwnd, pt, hmenu, -1);
\r
7320 switch (LOWORD(wParam)) {
\r
7322 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7324 case IDM_SelectAll:
\r
7326 sel.cpMax = -1; /*999999?*/
\r
7327 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7330 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7333 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7336 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7341 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7344 #define CO_MAX 100000
\r
7345 #define CO_TRIM 1000
\r
7348 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7350 static SnapData sd;
\r
7351 HWND hText, hInput;
\r
7353 static int sizeX, sizeY;
\r
7354 int newSizeX, newSizeY;
\r
7358 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7359 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7361 switch (message) {
\r
7363 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7365 ENLINK *pLink = (ENLINK*)lParam;
\r
7366 if (pLink->msg == WM_LBUTTONUP)
\r
7370 tr.chrg = pLink->chrg;
\r
7371 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7372 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7373 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7374 free(tr.lpstrText);
\r
7378 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7379 hwndConsole = hDlg;
\r
7381 consoleTextWindowProc = (WNDPROC)
\r
7382 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7383 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7384 consoleInputWindowProc = (WNDPROC)
\r
7385 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7386 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7387 Colorize(ColorNormal, TRUE);
\r
7388 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7389 ChangedConsoleFont();
\r
7390 GetClientRect(hDlg, &rect);
\r
7391 sizeX = rect.right;
\r
7392 sizeY = rect.bottom;
\r
7393 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7394 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7395 WINDOWPLACEMENT wp;
\r
7396 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7397 wp.length = sizeof(WINDOWPLACEMENT);
\r
7399 wp.showCmd = SW_SHOW;
\r
7400 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7401 wp.rcNormalPosition.left = wpConsole.x;
\r
7402 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7403 wp.rcNormalPosition.top = wpConsole.y;
\r
7404 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7405 SetWindowPlacement(hDlg, &wp);
\r
7408 // [HGM] Chessknight's change 2004-07-13
\r
7409 else { /* Determine Defaults */
\r
7410 WINDOWPLACEMENT wp;
\r
7411 wpConsole.x = wpMain.width + 1;
\r
7412 wpConsole.y = wpMain.y;
\r
7413 wpConsole.width = screenWidth - wpMain.width;
\r
7414 wpConsole.height = wpMain.height;
\r
7415 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7416 wp.length = sizeof(WINDOWPLACEMENT);
\r
7418 wp.showCmd = SW_SHOW;
\r
7419 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7420 wp.rcNormalPosition.left = wpConsole.x;
\r
7421 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7422 wp.rcNormalPosition.top = wpConsole.y;
\r
7423 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7424 SetWindowPlacement(hDlg, &wp);
\r
7427 // Allow hText to highlight URLs and send notifications on them
\r
7428 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7429 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7430 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7431 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7445 if (IsIconic(hDlg)) break;
\r
7446 newSizeX = LOWORD(lParam);
\r
7447 newSizeY = HIWORD(lParam);
\r
7448 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7449 RECT rectText, rectInput;
\r
7451 int newTextHeight, newTextWidth;
\r
7452 GetWindowRect(hText, &rectText);
\r
7453 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7454 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7455 if (newTextHeight < 0) {
\r
7456 newSizeY += -newTextHeight;
\r
7457 newTextHeight = 0;
\r
7459 SetWindowPos(hText, NULL, 0, 0,
\r
7460 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7461 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7462 pt.x = rectInput.left;
\r
7463 pt.y = rectInput.top + newSizeY - sizeY;
\r
7464 ScreenToClient(hDlg, &pt);
\r
7465 SetWindowPos(hInput, NULL,
\r
7466 pt.x, pt.y, /* needs client coords */
\r
7467 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7468 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7474 case WM_GETMINMAXINFO:
\r
7475 /* Prevent resizing window too small */
\r
7476 mmi = (MINMAXINFO *) lParam;
\r
7477 mmi->ptMinTrackSize.x = 100;
\r
7478 mmi->ptMinTrackSize.y = 100;
\r
7481 /* [AS] Snapping */
\r
7482 case WM_ENTERSIZEMOVE:
\r
7483 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7486 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7489 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7491 case WM_EXITSIZEMOVE:
\r
7492 UpdateICSWidth(hText);
\r
7493 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7496 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7504 if (hwndConsole) return;
\r
7505 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7506 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7511 ConsoleOutput(char* data, int length, int forceVisible)
\r
7516 char buf[CO_MAX+1];
\r
7519 static int delayLF = 0;
\r
7520 CHARRANGE savesel, sel;
\r
7522 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7530 while (length--) {
\r
7538 } else if (*p == '\007') {
\r
7539 MyPlaySound(&sounds[(int)SoundBell]);
\r
7546 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7547 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7548 /* Save current selection */
\r
7549 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7550 exlen = GetWindowTextLength(hText);
\r
7551 /* Find out whether current end of text is visible */
\r
7552 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7553 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7554 /* Trim existing text if it's too long */
\r
7555 if (exlen + (q - buf) > CO_MAX) {
\r
7556 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7559 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7560 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7562 savesel.cpMin -= trim;
\r
7563 savesel.cpMax -= trim;
\r
7564 if (exlen < 0) exlen = 0;
\r
7565 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7566 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7568 /* Append the new text */
\r
7569 sel.cpMin = exlen;
\r
7570 sel.cpMax = exlen;
\r
7571 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7572 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7573 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7574 if (forceVisible || exlen == 0 ||
\r
7575 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7576 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7577 /* Scroll to make new end of text visible if old end of text
\r
7578 was visible or new text is an echo of user typein */
\r
7579 sel.cpMin = 9999999;
\r
7580 sel.cpMax = 9999999;
\r
7581 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7582 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7583 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7584 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7586 if (savesel.cpMax == exlen || forceVisible) {
\r
7587 /* Move insert point to new end of text if it was at the old
\r
7588 end of text or if the new text is an echo of user typein */
\r
7589 sel.cpMin = 9999999;
\r
7590 sel.cpMax = 9999999;
\r
7591 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7593 /* Restore previous selection */
\r
7594 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7596 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7603 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7607 COLORREF oldFg, oldBg;
\r
7611 if(copyNumber > 1)
\r
7612 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7614 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7615 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7616 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7619 rect.right = x + squareSize;
\r
7621 rect.bottom = y + squareSize;
\r
7624 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7625 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7626 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7627 &rect, str, strlen(str), NULL);
\r
7629 (void) SetTextColor(hdc, oldFg);
\r
7630 (void) SetBkColor(hdc, oldBg);
\r
7631 (void) SelectObject(hdc, oldFont);
\r
7635 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7636 RECT *rect, char *color, char *flagFell)
\r
7640 COLORREF oldFg, oldBg;
\r
7643 if (twoBoards && partnerUp) return;
\r
7644 if (appData.clockMode) {
\r
7646 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7648 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7655 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7656 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7658 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7659 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7661 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7665 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7666 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7667 rect, str, strlen(str), NULL);
\r
7668 if(logoHeight > 0 && appData.clockMode) {
\r
7670 str += strlen(color)+2;
\r
7671 r.top = rect->top + logoHeight/2;
\r
7672 r.left = rect->left;
\r
7673 r.right = rect->right;
\r
7674 r.bottom = rect->bottom;
\r
7675 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7676 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7677 &r, str, strlen(str), NULL);
\r
7679 (void) SetTextColor(hdc, oldFg);
\r
7680 (void) SetBkColor(hdc, oldBg);
\r
7681 (void) SelectObject(hdc, oldFont);
\r
7686 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7692 if( count <= 0 ) {
\r
7693 if (appData.debugMode) {
\r
7694 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7697 return ERROR_INVALID_USER_BUFFER;
\r
7700 ResetEvent(ovl->hEvent);
\r
7701 ovl->Offset = ovl->OffsetHigh = 0;
\r
7702 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7706 err = GetLastError();
\r
7707 if (err == ERROR_IO_PENDING) {
\r
7708 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7712 err = GetLastError();
\r
7719 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7724 ResetEvent(ovl->hEvent);
\r
7725 ovl->Offset = ovl->OffsetHigh = 0;
\r
7726 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7730 err = GetLastError();
\r
7731 if (err == ERROR_IO_PENDING) {
\r
7732 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7736 err = GetLastError();
\r
7743 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7744 void CheckForInputBufferFull( InputSource * is )
\r
7746 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7747 /* Look for end of line */
\r
7748 char * p = is->buf;
\r
7750 while( p < is->next && *p != '\n' ) {
\r
7754 if( p >= is->next ) {
\r
7755 if (appData.debugMode) {
\r
7756 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7759 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7760 is->count = (DWORD) -1;
\r
7761 is->next = is->buf;
\r
7767 InputThread(LPVOID arg)
\r
7772 is = (InputSource *) arg;
\r
7773 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7774 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7775 while (is->hThread != NULL) {
\r
7776 is->error = DoReadFile(is->hFile, is->next,
\r
7777 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7778 &is->count, &ovl);
\r
7779 if (is->error == NO_ERROR) {
\r
7780 is->next += is->count;
\r
7782 if (is->error == ERROR_BROKEN_PIPE) {
\r
7783 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7786 is->count = (DWORD) -1;
\r
7787 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7792 CheckForInputBufferFull( is );
\r
7794 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7796 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7798 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7801 CloseHandle(ovl.hEvent);
\r
7802 CloseHandle(is->hFile);
\r
7804 if (appData.debugMode) {
\r
7805 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7812 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7814 NonOvlInputThread(LPVOID arg)
\r
7821 is = (InputSource *) arg;
\r
7822 while (is->hThread != NULL) {
\r
7823 is->error = ReadFile(is->hFile, is->next,
\r
7824 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7825 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7826 if (is->error == NO_ERROR) {
\r
7827 /* Change CRLF to LF */
\r
7828 if (is->next > is->buf) {
\r
7830 i = is->count + 1;
\r
7838 if (prev == '\r' && *p == '\n') {
\r
7850 if (is->error == ERROR_BROKEN_PIPE) {
\r
7851 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7854 is->count = (DWORD) -1;
\r
7858 CheckForInputBufferFull( is );
\r
7860 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7862 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7864 if (is->count < 0) break; /* Quit on error */
\r
7866 CloseHandle(is->hFile);
\r
7871 SocketInputThread(LPVOID arg)
\r
7875 is = (InputSource *) arg;
\r
7876 while (is->hThread != NULL) {
\r
7877 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7878 if ((int)is->count == SOCKET_ERROR) {
\r
7879 is->count = (DWORD) -1;
\r
7880 is->error = WSAGetLastError();
\r
7882 is->error = NO_ERROR;
\r
7883 is->next += is->count;
\r
7884 if (is->count == 0 && is->second == is) {
\r
7885 /* End of file on stderr; quit with no message */
\r
7889 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7891 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7893 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7899 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7903 is = (InputSource *) lParam;
\r
7904 if (is->lineByLine) {
\r
7905 /* Feed in lines one by one */
\r
7906 char *p = is->buf;
\r
7908 while (q < is->next) {
\r
7909 if (*q++ == '\n') {
\r
7910 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7915 /* Move any partial line to the start of the buffer */
\r
7917 while (p < is->next) {
\r
7922 if (is->error != NO_ERROR || is->count == 0) {
\r
7923 /* Notify backend of the error. Note: If there was a partial
\r
7924 line at the end, it is not flushed through. */
\r
7925 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7928 /* Feed in the whole chunk of input at once */
\r
7929 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7930 is->next = is->buf;
\r
7934 /*---------------------------------------------------------------------------*\
\r
7936 * Menu enables. Used when setting various modes.
\r
7938 \*---------------------------------------------------------------------------*/
\r
7946 GreyRevert(Boolean grey)
\r
7947 { // [HGM] vari: for retracting variations in local mode
\r
7948 HMENU hmenu = GetMenu(hwndMain);
\r
7949 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7950 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7954 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7956 while (enab->item > 0) {
\r
7957 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7962 Enables gnuEnables[] = {
\r
7963 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7964 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7965 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7966 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7967 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7968 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7969 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7970 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7971 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7972 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7973 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7974 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7975 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7977 // Needed to switch from ncp to GNU mode on Engine Load
\r
7978 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7979 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7980 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7981 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7982 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7983 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7984 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
7985 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7986 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
7987 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
7988 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7989 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7990 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7991 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7995 Enables icsEnables[] = {
\r
7996 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7997 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7998 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7999 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8000 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8001 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8002 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
8003 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
8004 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
8005 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
8006 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8007 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
8008 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
8009 { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },
\r
8010 { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },
\r
8011 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
8012 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
8013 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
8014 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
8015 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
8020 Enables zippyEnables[] = {
\r
8021 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8022 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
8023 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
8024 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
8029 Enables ncpEnables[] = {
\r
8030 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
8031 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
8032 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8033 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8034 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8035 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8036 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
8037 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
8038 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
8039 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
8040 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
8041 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
8042 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8043 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
8044 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
8045 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
8046 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
8047 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
8048 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
8049 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
8050 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
8051 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
8055 Enables trainingOnEnables[] = {
\r
8056 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
8057 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
8058 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
8059 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
8060 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
8061 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
8062 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
8063 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8064 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
8068 Enables trainingOffEnables[] = {
\r
8069 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
8070 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
8071 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
8072 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
8073 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
8074 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
8075 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
8076 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8077 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
8081 /* These modify either ncpEnables or gnuEnables */
\r
8082 Enables cmailEnables[] = {
\r
8083 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
8084 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
8085 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
8086 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
8087 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
8088 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
8089 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
8093 Enables machineThinkingEnables[] = {
\r
8094 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8095 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
8096 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
8097 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8098 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
8099 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8100 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8101 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8102 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8103 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
8104 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8105 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8106 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8107 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8108 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
8109 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
8113 Enables userThinkingEnables[] = {
\r
8114 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8115 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
8116 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
8117 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8118 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
8119 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8120 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8121 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8122 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8123 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
8124 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
8125 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
8126 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
8127 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
8128 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
8129 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
8133 /*---------------------------------------------------------------------------*\
\r
8135 * Front-end interface functions exported by XBoard.
\r
8136 * Functions appear in same order as prototypes in frontend.h.
\r
8138 \*---------------------------------------------------------------------------*/
\r
8140 CheckMark(UINT item, int state)
\r
8142 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
8148 static UINT prevChecked = 0;
\r
8149 static int prevPausing = 0;
\r
8152 if (pausing != prevPausing) {
\r
8153 prevPausing = pausing;
\r
8154 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
8155 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
8156 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
8159 switch (gameMode) {
\r
8160 case BeginningOfGame:
\r
8161 if (appData.icsActive)
\r
8162 nowChecked = IDM_IcsClient;
\r
8163 else if (appData.noChessProgram)
\r
8164 nowChecked = IDM_EditGame;
\r
8166 nowChecked = IDM_MachineBlack;
\r
8168 case MachinePlaysBlack:
\r
8169 nowChecked = IDM_MachineBlack;
\r
8171 case MachinePlaysWhite:
\r
8172 nowChecked = IDM_MachineWhite;
\r
8174 case TwoMachinesPlay:
\r
8175 nowChecked = IDM_TwoMachines;
\r
8178 nowChecked = IDM_AnalysisMode;
\r
8181 nowChecked = IDM_AnalyzeFile;
\r
8184 nowChecked = IDM_EditGame;
\r
8186 case PlayFromGameFile:
\r
8187 nowChecked = IDM_LoadGame;
\r
8189 case EditPosition:
\r
8190 nowChecked = IDM_EditPosition;
\r
8193 nowChecked = IDM_Training;
\r
8195 case IcsPlayingWhite:
\r
8196 case IcsPlayingBlack:
\r
8197 case IcsObserving:
\r
8199 nowChecked = IDM_IcsClient;
\r
8206 CheckMark(prevChecked, MF_UNCHECKED);
\r
8207 CheckMark(nowChecked, MF_CHECKED);
\r
8208 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8210 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8211 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8212 MF_BYCOMMAND|MF_ENABLED);
\r
8214 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8215 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8218 prevChecked = nowChecked;
\r
8220 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8221 if (appData.icsActive) {
\r
8222 if (appData.icsEngineAnalyze) {
\r
8223 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8225 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8228 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8234 HMENU hmenu = GetMenu(hwndMain);
\r
8235 SetMenuEnables(hmenu, icsEnables);
\r
8236 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8237 MF_BYCOMMAND|MF_ENABLED);
\r
8239 if (appData.zippyPlay) {
\r
8240 SetMenuEnables(hmenu, zippyEnables);
\r
8241 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8242 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8243 MF_BYCOMMAND|MF_ENABLED);
\r
8251 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8257 HMENU hmenu = GetMenu(hwndMain);
\r
8258 SetMenuEnables(hmenu, ncpEnables);
\r
8259 DrawMenuBar(hwndMain);
\r
8265 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8269 SetTrainingModeOn()
\r
8272 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8273 for (i = 0; i < N_BUTTONS; i++) {
\r
8274 if (buttonDesc[i].hwnd != NULL)
\r
8275 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8280 VOID SetTrainingModeOff()
\r
8283 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8284 for (i = 0; i < N_BUTTONS; i++) {
\r
8285 if (buttonDesc[i].hwnd != NULL)
\r
8286 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8292 SetUserThinkingEnables()
\r
8294 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8298 SetMachineThinkingEnables()
\r
8300 HMENU hMenu = GetMenu(hwndMain);
\r
8301 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8303 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8305 if (gameMode == MachinePlaysBlack) {
\r
8306 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8307 } else if (gameMode == MachinePlaysWhite) {
\r
8308 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8309 } else if (gameMode == TwoMachinesPlay) {
\r
8310 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8316 DisplayTitle(char *str)
\r
8318 char title[MSG_SIZ], *host;
\r
8319 if (str[0] != NULLCHAR) {
\r
8320 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8321 } else if (appData.icsActive) {
\r
8322 if (appData.icsCommPort[0] != NULLCHAR)
\r
8325 host = appData.icsHost;
\r
8326 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8327 } else if (appData.noChessProgram) {
\r
8328 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8330 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8331 strcat(title, ": ");
\r
8332 strcat(title, first.tidy);
\r
8334 SetWindowText(hwndMain, title);
\r
8339 DisplayMessage(char *str1, char *str2)
\r
8343 int remain = MESSAGE_TEXT_MAX - 1;
\r
8346 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8347 messageText[0] = NULLCHAR;
\r
8349 len = strlen(str1);
\r
8350 if (len > remain) len = remain;
\r
8351 strncpy(messageText, str1, len);
\r
8352 messageText[len] = NULLCHAR;
\r
8355 if (*str2 && remain >= 2) {
\r
8357 strcat(messageText, " ");
\r
8360 len = strlen(str2);
\r
8361 if (len > remain) len = remain;
\r
8362 strncat(messageText, str2, len);
\r
8364 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8365 safeStrCpy(lastMsg, messageText, MSG_SIZ);
\r
8367 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8371 hdc = GetDC(hwndMain);
\r
8372 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8373 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8374 &messageRect, messageText, strlen(messageText), NULL);
\r
8375 (void) SelectObject(hdc, oldFont);
\r
8376 (void) ReleaseDC(hwndMain, hdc);
\r
8380 DisplayError(char *str, int error)
\r
8382 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8386 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8388 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8389 NULL, error, LANG_NEUTRAL,
\r
8390 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8392 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8394 ErrorMap *em = errmap;
\r
8395 while (em->err != 0 && em->err != error) em++;
\r
8396 if (em->err != 0) {
\r
8397 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8399 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8404 ErrorPopUp(_("Error"), buf);
\r
8409 DisplayMoveError(char *str)
\r
8411 fromX = fromY = -1;
\r
8412 ClearHighlights();
\r
8413 DrawPosition(FALSE, NULL);
\r
8414 if (appData.popupMoveErrors) {
\r
8415 ErrorPopUp(_("Error"), str);
\r
8417 DisplayMessage(str, "");
\r
8418 moveErrorMessageUp = TRUE;
\r
8423 DisplayFatalError(char *str, int error, int exitStatus)
\r
8425 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8427 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8430 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8431 NULL, error, LANG_NEUTRAL,
\r
8432 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8434 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8436 ErrorMap *em = errmap;
\r
8437 while (em->err != 0 && em->err != error) em++;
\r
8438 if (em->err != 0) {
\r
8439 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8441 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8446 if (appData.debugMode) {
\r
8447 fprintf(debugFP, "%s: %s\n", label, str);
\r
8449 if (appData.popupExitMessage) {
\r
8450 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8451 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8453 ExitEvent(exitStatus);
\r
8458 DisplayInformation(char *str)
\r
8460 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8465 DisplayNote(char *str)
\r
8467 ErrorPopUp(_("Note"), str);
\r
8472 char *title, *question, *replyPrefix;
\r
8477 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8479 static QuestionParams *qp;
\r
8480 char reply[MSG_SIZ];
\r
8483 switch (message) {
\r
8484 case WM_INITDIALOG:
\r
8485 qp = (QuestionParams *) lParam;
\r
8486 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8487 Translate(hDlg, DLG_Question);
\r
8488 SetWindowText(hDlg, qp->title);
\r
8489 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8490 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8494 switch (LOWORD(wParam)) {
\r
8496 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8497 if (*reply) strcat(reply, " ");
\r
8498 len = strlen(reply);
\r
8499 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8500 strcat(reply, "\n");
\r
8501 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8502 EndDialog(hDlg, TRUE);
\r
8503 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8506 EndDialog(hDlg, FALSE);
\r
8517 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8519 QuestionParams qp;
\r
8523 qp.question = question;
\r
8524 qp.replyPrefix = replyPrefix;
\r
8526 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8527 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8528 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8529 FreeProcInstance(lpProc);
\r
8532 /* [AS] Pick FRC position */
\r
8533 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8535 static int * lpIndexFRC;
\r
8541 case WM_INITDIALOG:
\r
8542 lpIndexFRC = (int *) lParam;
\r
8544 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8545 Translate(hDlg, DLG_NewGameFRC);
\r
8547 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8548 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8549 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8550 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8555 switch( LOWORD(wParam) ) {
\r
8557 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8558 EndDialog( hDlg, 0 );
\r
8559 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8562 EndDialog( hDlg, 1 );
\r
8564 case IDC_NFG_Edit:
\r
8565 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8566 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8568 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8571 case IDC_NFG_Random:
\r
8572 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8573 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8586 int index = appData.defaultFrcPosition;
\r
8587 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8589 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8591 if( result == 0 ) {
\r
8592 appData.defaultFrcPosition = index;
\r
8598 /* [AS] Game list options. Refactored by HGM */
\r
8600 HWND gameListOptionsDialog;
\r
8602 // low-level front-end: clear text edit / list widget
\r
8607 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8610 // low-level front-end: clear text edit / list widget
\r
8612 GLT_DeSelectList()
\r
8614 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8617 // low-level front-end: append line to text edit / list widget
\r
8619 GLT_AddToList( char *name )
\r
8622 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8626 // low-level front-end: get line from text edit / list widget
\r
8628 GLT_GetFromList( int index, char *name )
\r
8631 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8637 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8639 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8640 int idx2 = idx1 + delta;
\r
8641 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8643 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8646 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8647 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8648 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8649 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8653 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8657 case WM_INITDIALOG:
\r
8658 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8660 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8661 Translate(hDlg, DLG_GameListOptions);
\r
8663 /* Initialize list */
\r
8664 GLT_TagsToList( lpUserGLT );
\r
8666 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8671 switch( LOWORD(wParam) ) {
\r
8674 EndDialog( hDlg, 0 );
\r
8677 EndDialog( hDlg, 1 );
\r
8680 case IDC_GLT_Default:
\r
8681 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8684 case IDC_GLT_Restore:
\r
8685 GLT_TagsToList( appData.gameListTags );
\r
8689 GLT_MoveSelection( hDlg, -1 );
\r
8692 case IDC_GLT_Down:
\r
8693 GLT_MoveSelection( hDlg, +1 );
\r
8703 int GameListOptions()
\r
8706 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8708 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8710 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8712 if( result == 0 ) {
\r
8713 /* [AS] Memory leak here! */
\r
8714 appData.gameListTags = strdup( lpUserGLT );
\r
8721 DisplayIcsInteractionTitle(char *str)
\r
8723 char consoleTitle[MSG_SIZ];
\r
8725 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8726 SetWindowText(hwndConsole, consoleTitle);
\r
8728 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
8729 char buf[MSG_SIZ], *p = buf, *q;
\r
8730 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
8732 q = strchr(p, ';');
\r
8734 if(*p) ChatPopUp(p);
\r
8738 SetActiveWindow(hwndMain);
\r
8742 DrawPosition(int fullRedraw, Board board)
\r
8744 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8747 void NotifyFrontendLogin()
\r
8750 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8756 fromX = fromY = -1;
\r
8757 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8758 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8759 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8760 dragInfo.lastpos = dragInfo.pos;
\r
8761 dragInfo.start.x = dragInfo.start.y = -1;
\r
8762 dragInfo.from = dragInfo.start;
\r
8764 DrawPosition(TRUE, NULL);
\r
8771 CommentPopUp(char *title, char *str)
\r
8773 HWND hwnd = GetActiveWindow();
\r
8774 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8776 SetActiveWindow(hwnd);
\r
8780 CommentPopDown(void)
\r
8782 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8783 if (commentDialog) {
\r
8784 ShowWindow(commentDialog, SW_HIDE);
\r
8786 commentUp = FALSE;
\r
8790 EditCommentPopUp(int index, char *title, char *str)
\r
8792 EitherCommentPopUp(index, title, str, TRUE);
\r
8799 MyPlaySound(&sounds[(int)SoundRoar]);
\r
8806 MyPlaySound(&sounds[(int)SoundMove]);
\r
8809 VOID PlayIcsWinSound()
\r
8811 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8814 VOID PlayIcsLossSound()
\r
8816 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8819 VOID PlayIcsDrawSound()
\r
8821 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8824 VOID PlayIcsUnfinishedSound()
\r
8826 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8832 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8838 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8846 consoleEcho = TRUE;
\r
8847 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8848 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8849 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8858 consoleEcho = FALSE;
\r
8859 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8860 /* This works OK: set text and background both to the same color */
\r
8862 cf.crTextColor = COLOR_ECHOOFF;
\r
8863 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8864 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8867 /* No Raw()...? */
\r
8869 void Colorize(ColorClass cc, int continuation)
\r
8871 currentColorClass = cc;
\r
8872 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8873 consoleCF.crTextColor = textAttribs[cc].color;
\r
8874 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8875 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8881 static char buf[MSG_SIZ];
\r
8882 DWORD bufsiz = MSG_SIZ;
\r
8884 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8885 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8887 if (!GetUserName(buf, &bufsiz)) {
\r
8888 /*DisplayError("Error getting user name", GetLastError());*/
\r
8889 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8897 static char buf[MSG_SIZ];
\r
8898 DWORD bufsiz = MSG_SIZ;
\r
8900 if (!GetComputerName(buf, &bufsiz)) {
\r
8901 /*DisplayError("Error getting host name", GetLastError());*/
\r
8902 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8909 ClockTimerRunning()
\r
8911 return clockTimerEvent != 0;
\r
8917 if (clockTimerEvent == 0) return FALSE;
\r
8918 KillTimer(hwndMain, clockTimerEvent);
\r
8919 clockTimerEvent = 0;
\r
8924 StartClockTimer(long millisec)
\r
8926 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8927 (UINT) millisec, NULL);
\r
8931 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8934 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8936 if(appData.noGUI) return;
\r
8937 hdc = GetDC(hwndMain);
\r
8938 if (!IsIconic(hwndMain)) {
\r
8939 DisplayAClock(hdc, timeRemaining, highlight,
\r
8940 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8942 if (highlight && iconCurrent == iconBlack) {
\r
8943 iconCurrent = iconWhite;
\r
8944 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8945 if (IsIconic(hwndMain)) {
\r
8946 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8949 (void) ReleaseDC(hwndMain, hdc);
\r
8951 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8955 DisplayBlackClock(long timeRemaining, int highlight)
\r
8958 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8961 if(appData.noGUI) return;
\r
8962 hdc = GetDC(hwndMain);
\r
8963 if (!IsIconic(hwndMain)) {
\r
8964 DisplayAClock(hdc, timeRemaining, highlight,
\r
8965 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8967 if (highlight && iconCurrent == iconWhite) {
\r
8968 iconCurrent = iconBlack;
\r
8969 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8970 if (IsIconic(hwndMain)) {
\r
8971 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8974 (void) ReleaseDC(hwndMain, hdc);
\r
8976 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8981 LoadGameTimerRunning()
\r
8983 return loadGameTimerEvent != 0;
\r
8987 StopLoadGameTimer()
\r
8989 if (loadGameTimerEvent == 0) return FALSE;
\r
8990 KillTimer(hwndMain, loadGameTimerEvent);
\r
8991 loadGameTimerEvent = 0;
\r
8996 StartLoadGameTimer(long millisec)
\r
8998 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8999 (UINT) millisec, NULL);
\r
9007 char fileTitle[MSG_SIZ];
\r
9009 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
9010 f = OpenFileDialog(hwndMain, "a", defName,
\r
9011 appData.oldSaveStyle ? "gam" : "pgn",
\r
9013 _("Save Game to File"), NULL, fileTitle, NULL);
\r
9015 SaveGame(f, 0, "");
\r
9022 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
9024 if (delayedTimerEvent != 0) {
\r
9025 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
9026 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
9028 KillTimer(hwndMain, delayedTimerEvent);
\r
9029 delayedTimerEvent = 0;
\r
9030 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
9031 delayedTimerCallback();
\r
9033 delayedTimerCallback = cb;
\r
9034 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
9035 (UINT) millisec, NULL);
\r
9038 DelayedEventCallback
\r
9041 if (delayedTimerEvent) {
\r
9042 return delayedTimerCallback;
\r
9049 CancelDelayedEvent()
\r
9051 if (delayedTimerEvent) {
\r
9052 KillTimer(hwndMain, delayedTimerEvent);
\r
9053 delayedTimerEvent = 0;
\r
9057 DWORD GetWin32Priority(int nice)
\r
9058 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
9060 REALTIME_PRIORITY_CLASS 0x00000100
\r
9061 HIGH_PRIORITY_CLASS 0x00000080
\r
9062 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
9063 NORMAL_PRIORITY_CLASS 0x00000020
\r
9064 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
9065 IDLE_PRIORITY_CLASS 0x00000040
\r
9067 if (nice < -15) return 0x00000080;
\r
9068 if (nice < 0) return 0x00008000;
\r
9069 if (nice == 0) return 0x00000020;
\r
9070 if (nice < 15) return 0x00004000;
\r
9071 return 0x00000040;
\r
9074 void RunCommand(char *cmdLine)
\r
9076 /* Now create the child process. */
\r
9077 STARTUPINFO siStartInfo;
\r
9078 PROCESS_INFORMATION piProcInfo;
\r
9080 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9081 siStartInfo.lpReserved = NULL;
\r
9082 siStartInfo.lpDesktop = NULL;
\r
9083 siStartInfo.lpTitle = NULL;
\r
9084 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9085 siStartInfo.cbReserved2 = 0;
\r
9086 siStartInfo.lpReserved2 = NULL;
\r
9087 siStartInfo.hStdInput = NULL;
\r
9088 siStartInfo.hStdOutput = NULL;
\r
9089 siStartInfo.hStdError = NULL;
\r
9091 CreateProcess(NULL,
\r
9092 cmdLine, /* command line */
\r
9093 NULL, /* process security attributes */
\r
9094 NULL, /* primary thread security attrs */
\r
9095 TRUE, /* handles are inherited */
\r
9096 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9097 NULL, /* use parent's environment */
\r
9099 &siStartInfo, /* STARTUPINFO pointer */
\r
9100 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9102 CloseHandle(piProcInfo.hThread);
\r
9105 /* Start a child process running the given program.
\r
9106 The process's standard output can be read from "from", and its
\r
9107 standard input can be written to "to".
\r
9108 Exit with fatal error if anything goes wrong.
\r
9109 Returns an opaque pointer that can be used to destroy the process
\r
9113 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
9115 #define BUFSIZE 4096
\r
9117 HANDLE hChildStdinRd, hChildStdinWr,
\r
9118 hChildStdoutRd, hChildStdoutWr;
\r
9119 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
9120 SECURITY_ATTRIBUTES saAttr;
\r
9122 PROCESS_INFORMATION piProcInfo;
\r
9123 STARTUPINFO siStartInfo;
\r
9125 char buf[MSG_SIZ];
\r
9128 if (appData.debugMode) {
\r
9129 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
9134 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
9135 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
9136 saAttr.bInheritHandle = TRUE;
\r
9137 saAttr.lpSecurityDescriptor = NULL;
\r
9140 * The steps for redirecting child's STDOUT:
\r
9141 * 1. Create anonymous pipe to be STDOUT for child.
\r
9142 * 2. Create a noninheritable duplicate of read handle,
\r
9143 * and close the inheritable read handle.
\r
9146 /* Create a pipe for the child's STDOUT. */
\r
9147 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
9148 return GetLastError();
\r
9151 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
9152 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
9153 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
9154 FALSE, /* not inherited */
\r
9155 DUPLICATE_SAME_ACCESS);
\r
9157 return GetLastError();
\r
9159 CloseHandle(hChildStdoutRd);
\r
9162 * The steps for redirecting child's STDIN:
\r
9163 * 1. Create anonymous pipe to be STDIN for child.
\r
9164 * 2. Create a noninheritable duplicate of write handle,
\r
9165 * and close the inheritable write handle.
\r
9168 /* Create a pipe for the child's STDIN. */
\r
9169 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
9170 return GetLastError();
\r
9173 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
9174 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
9175 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
9176 FALSE, /* not inherited */
\r
9177 DUPLICATE_SAME_ACCESS);
\r
9179 return GetLastError();
\r
9181 CloseHandle(hChildStdinWr);
\r
9183 /* Arrange to (1) look in dir for the child .exe file, and
\r
9184 * (2) have dir be the child's working directory. Interpret
\r
9185 * dir relative to the directory WinBoard loaded from. */
\r
9186 GetCurrentDirectory(MSG_SIZ, buf);
\r
9187 SetCurrentDirectory(installDir);
\r
9188 SetCurrentDirectory(dir);
\r
9190 /* Now create the child process. */
\r
9192 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9193 siStartInfo.lpReserved = NULL;
\r
9194 siStartInfo.lpDesktop = NULL;
\r
9195 siStartInfo.lpTitle = NULL;
\r
9196 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9197 siStartInfo.cbReserved2 = 0;
\r
9198 siStartInfo.lpReserved2 = NULL;
\r
9199 siStartInfo.hStdInput = hChildStdinRd;
\r
9200 siStartInfo.hStdOutput = hChildStdoutWr;
\r
9201 siStartInfo.hStdError = hChildStdoutWr;
\r
9203 fSuccess = CreateProcess(NULL,
\r
9204 cmdLine, /* command line */
\r
9205 NULL, /* process security attributes */
\r
9206 NULL, /* primary thread security attrs */
\r
9207 TRUE, /* handles are inherited */
\r
9208 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9209 NULL, /* use parent's environment */
\r
9211 &siStartInfo, /* STARTUPINFO pointer */
\r
9212 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9214 err = GetLastError();
\r
9215 SetCurrentDirectory(buf); /* return to prev directory */
\r
9220 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
9221 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
9222 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
9225 /* Close the handles we don't need in the parent */
\r
9226 CloseHandle(piProcInfo.hThread);
\r
9227 CloseHandle(hChildStdinRd);
\r
9228 CloseHandle(hChildStdoutWr);
\r
9230 /* Prepare return value */
\r
9231 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9232 cp->kind = CPReal;
\r
9233 cp->hProcess = piProcInfo.hProcess;
\r
9234 cp->pid = piProcInfo.dwProcessId;
\r
9235 cp->hFrom = hChildStdoutRdDup;
\r
9236 cp->hTo = hChildStdinWrDup;
\r
9238 *pr = (void *) cp;
\r
9240 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
9241 2000 where engines sometimes don't see the initial command(s)
\r
9242 from WinBoard and hang. I don't understand how that can happen,
\r
9243 but the Sleep is harmless, so I've put it in. Others have also
\r
9244 reported what may be the same problem, so hopefully this will fix
\r
9245 it for them too. */
\r
9253 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
9255 ChildProc *cp; int result;
\r
9257 cp = (ChildProc *) pr;
\r
9258 if (cp == NULL) return;
\r
9260 switch (cp->kind) {
\r
9262 /* TerminateProcess is considered harmful, so... */
\r
9263 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9264 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9265 /* The following doesn't work because the chess program
\r
9266 doesn't "have the same console" as WinBoard. Maybe
\r
9267 we could arrange for this even though neither WinBoard
\r
9268 nor the chess program uses a console for stdio? */
\r
9269 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9271 /* [AS] Special termination modes for misbehaving programs... */
\r
9272 if( signal & 8 ) {
\r
9273 result = TerminateProcess( cp->hProcess, 0 );
\r
9275 if ( appData.debugMode) {
\r
9276 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9279 else if( signal & 4 ) {
\r
9280 DWORD dw = WaitForSingleObject( cp->hProcess, appData.delayAfterQuit*1000 + 50 ); // Wait 3 seconds at most
\r
9282 if( dw != WAIT_OBJECT_0 ) {
\r
9283 result = TerminateProcess( cp->hProcess, 0 );
\r
9285 if ( appData.debugMode) {
\r
9286 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9292 CloseHandle(cp->hProcess);
\r
9296 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9300 closesocket(cp->sock);
\r
9305 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9306 closesocket(cp->sock);
\r
9307 closesocket(cp->sock2);
\r
9315 InterruptChildProcess(ProcRef pr)
\r
9319 cp = (ChildProc *) pr;
\r
9320 if (cp == NULL) return;
\r
9321 switch (cp->kind) {
\r
9323 /* The following doesn't work because the chess program
\r
9324 doesn't "have the same console" as WinBoard. Maybe
\r
9325 we could arrange for this even though neither WinBoard
\r
9326 nor the chess program uses a console for stdio */
\r
9327 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9332 /* Can't interrupt */
\r
9336 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9343 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9345 char cmdLine[MSG_SIZ];
\r
9347 if (port[0] == NULLCHAR) {
\r
9348 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9350 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9352 return StartChildProcess(cmdLine, "", pr);
\r
9356 /* Code to open TCP sockets */
\r
9359 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9365 struct sockaddr_in sa, mysa;
\r
9366 struct hostent FAR *hp;
\r
9367 unsigned short uport;
\r
9368 WORD wVersionRequested;
\r
9371 /* Initialize socket DLL */
\r
9372 wVersionRequested = MAKEWORD(1, 1);
\r
9373 err = WSAStartup(wVersionRequested, &wsaData);
\r
9374 if (err != 0) return err;
\r
9377 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9378 err = WSAGetLastError();
\r
9383 /* Bind local address using (mostly) don't-care values.
\r
9385 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9386 mysa.sin_family = AF_INET;
\r
9387 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9388 uport = (unsigned short) 0;
\r
9389 mysa.sin_port = htons(uport);
\r
9390 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9391 == SOCKET_ERROR) {
\r
9392 err = WSAGetLastError();
\r
9397 /* Resolve remote host name */
\r
9398 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9399 if (!(hp = gethostbyname(host))) {
\r
9400 unsigned int b0, b1, b2, b3;
\r
9402 err = WSAGetLastError();
\r
9404 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9405 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9406 hp->h_addrtype = AF_INET;
\r
9408 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9409 hp->h_addr_list[0] = (char *) malloc(4);
\r
9410 hp->h_addr_list[0][0] = (char) b0;
\r
9411 hp->h_addr_list[0][1] = (char) b1;
\r
9412 hp->h_addr_list[0][2] = (char) b2;
\r
9413 hp->h_addr_list[0][3] = (char) b3;
\r
9419 sa.sin_family = hp->h_addrtype;
\r
9420 uport = (unsigned short) atoi(port);
\r
9421 sa.sin_port = htons(uport);
\r
9422 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9424 /* Make connection */
\r
9425 if (connect(s, (struct sockaddr *) &sa,
\r
9426 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9427 err = WSAGetLastError();
\r
9432 /* Prepare return value */
\r
9433 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9434 cp->kind = CPSock;
\r
9436 *pr = (ProcRef *) cp;
\r
9442 OpenCommPort(char *name, ProcRef *pr)
\r
9447 char fullname[MSG_SIZ];
\r
9449 if (*name != '\\')
\r
9450 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9452 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9454 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9455 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9456 if (h == (HANDLE) -1) {
\r
9457 return GetLastError();
\r
9461 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9463 /* Accumulate characters until a 100ms pause, then parse */
\r
9464 ct.ReadIntervalTimeout = 100;
\r
9465 ct.ReadTotalTimeoutMultiplier = 0;
\r
9466 ct.ReadTotalTimeoutConstant = 0;
\r
9467 ct.WriteTotalTimeoutMultiplier = 0;
\r
9468 ct.WriteTotalTimeoutConstant = 0;
\r
9469 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9471 /* Prepare return value */
\r
9472 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9473 cp->kind = CPComm;
\r
9476 *pr = (ProcRef *) cp;
\r
9482 OpenLoopback(ProcRef *pr)
\r
9484 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9490 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9495 struct sockaddr_in sa, mysa;
\r
9496 struct hostent FAR *hp;
\r
9497 unsigned short uport;
\r
9498 WORD wVersionRequested;
\r
9501 char stderrPortStr[MSG_SIZ];
\r
9503 /* Initialize socket DLL */
\r
9504 wVersionRequested = MAKEWORD(1, 1);
\r
9505 err = WSAStartup(wVersionRequested, &wsaData);
\r
9506 if (err != 0) return err;
\r
9508 /* Resolve remote host name */
\r
9509 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9510 if (!(hp = gethostbyname(host))) {
\r
9511 unsigned int b0, b1, b2, b3;
\r
9513 err = WSAGetLastError();
\r
9515 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9516 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9517 hp->h_addrtype = AF_INET;
\r
9519 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9520 hp->h_addr_list[0] = (char *) malloc(4);
\r
9521 hp->h_addr_list[0][0] = (char) b0;
\r
9522 hp->h_addr_list[0][1] = (char) b1;
\r
9523 hp->h_addr_list[0][2] = (char) b2;
\r
9524 hp->h_addr_list[0][3] = (char) b3;
\r
9530 sa.sin_family = hp->h_addrtype;
\r
9531 uport = (unsigned short) 514;
\r
9532 sa.sin_port = htons(uport);
\r
9533 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9535 /* Bind local socket to unused "privileged" port address
\r
9537 s = INVALID_SOCKET;
\r
9538 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9539 mysa.sin_family = AF_INET;
\r
9540 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9541 for (fromPort = 1023;; fromPort--) {
\r
9542 if (fromPort < 0) {
\r
9544 return WSAEADDRINUSE;
\r
9546 if (s == INVALID_SOCKET) {
\r
9547 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9548 err = WSAGetLastError();
\r
9553 uport = (unsigned short) fromPort;
\r
9554 mysa.sin_port = htons(uport);
\r
9555 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9556 == SOCKET_ERROR) {
\r
9557 err = WSAGetLastError();
\r
9558 if (err == WSAEADDRINUSE) continue;
\r
9562 if (connect(s, (struct sockaddr *) &sa,
\r
9563 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9564 err = WSAGetLastError();
\r
9565 if (err == WSAEADDRINUSE) {
\r
9576 /* Bind stderr local socket to unused "privileged" port address
\r
9578 s2 = INVALID_SOCKET;
\r
9579 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9580 mysa.sin_family = AF_INET;
\r
9581 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9582 for (fromPort = 1023;; fromPort--) {
\r
9583 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9584 if (fromPort < 0) {
\r
9585 (void) closesocket(s);
\r
9587 return WSAEADDRINUSE;
\r
9589 if (s2 == INVALID_SOCKET) {
\r
9590 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9591 err = WSAGetLastError();
\r
9597 uport = (unsigned short) fromPort;
\r
9598 mysa.sin_port = htons(uport);
\r
9599 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9600 == SOCKET_ERROR) {
\r
9601 err = WSAGetLastError();
\r
9602 if (err == WSAEADDRINUSE) continue;
\r
9603 (void) closesocket(s);
\r
9607 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9608 err = WSAGetLastError();
\r
9609 if (err == WSAEADDRINUSE) {
\r
9611 s2 = INVALID_SOCKET;
\r
9614 (void) closesocket(s);
\r
9615 (void) closesocket(s2);
\r
9621 prevStderrPort = fromPort; // remember port used
\r
9622 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9624 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9625 err = WSAGetLastError();
\r
9626 (void) closesocket(s);
\r
9627 (void) closesocket(s2);
\r
9632 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9633 err = WSAGetLastError();
\r
9634 (void) closesocket(s);
\r
9635 (void) closesocket(s2);
\r
9639 if (*user == NULLCHAR) user = UserName();
\r
9640 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9641 err = WSAGetLastError();
\r
9642 (void) closesocket(s);
\r
9643 (void) closesocket(s2);
\r
9647 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9648 err = WSAGetLastError();
\r
9649 (void) closesocket(s);
\r
9650 (void) closesocket(s2);
\r
9655 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9656 err = WSAGetLastError();
\r
9657 (void) closesocket(s);
\r
9658 (void) closesocket(s2);
\r
9662 (void) closesocket(s2); /* Stop listening */
\r
9664 /* Prepare return value */
\r
9665 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9666 cp->kind = CPRcmd;
\r
9669 *pr = (ProcRef *) cp;
\r
9676 AddInputSource(ProcRef pr, int lineByLine,
\r
9677 InputCallback func, VOIDSTAR closure)
\r
9679 InputSource *is, *is2 = NULL;
\r
9680 ChildProc *cp = (ChildProc *) pr;
\r
9682 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9683 is->lineByLine = lineByLine;
\r
9685 is->closure = closure;
\r
9686 is->second = NULL;
\r
9687 is->next = is->buf;
\r
9688 if (pr == NoProc) {
\r
9689 is->kind = CPReal;
\r
9690 consoleInputSource = is;
\r
9692 is->kind = cp->kind;
\r
9694 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9695 we create all threads suspended so that the is->hThread variable can be
\r
9696 safely assigned, then let the threads start with ResumeThread.
\r
9698 switch (cp->kind) {
\r
9700 is->hFile = cp->hFrom;
\r
9701 cp->hFrom = NULL; /* now owned by InputThread */
\r
9703 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9704 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9708 is->hFile = cp->hFrom;
\r
9709 cp->hFrom = NULL; /* now owned by InputThread */
\r
9711 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9712 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9716 is->sock = cp->sock;
\r
9718 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9719 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9723 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9725 is->sock = cp->sock;
\r
9727 is2->sock = cp->sock2;
\r
9728 is2->second = is2;
\r
9730 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9731 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9733 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9734 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9738 if( is->hThread != NULL ) {
\r
9739 ResumeThread( is->hThread );
\r
9742 if( is2 != NULL && is2->hThread != NULL ) {
\r
9743 ResumeThread( is2->hThread );
\r
9747 return (InputSourceRef) is;
\r
9751 RemoveInputSource(InputSourceRef isr)
\r
9755 is = (InputSource *) isr;
\r
9756 is->hThread = NULL; /* tell thread to stop */
\r
9757 CloseHandle(is->hThread);
\r
9758 if (is->second != NULL) {
\r
9759 is->second->hThread = NULL;
\r
9760 CloseHandle(is->second->hThread);
\r
9764 int no_wrap(char *message, int count)
\r
9766 ConsoleOutput(message, count, FALSE);
\r
9771 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9774 int outCount = SOCKET_ERROR;
\r
9775 ChildProc *cp = (ChildProc *) pr;
\r
9776 static OVERLAPPED ovl;
\r
9777 static int line = 0;
\r
9781 if (appData.noJoin || !appData.useInternalWrap)
\r
9782 return no_wrap(message, count);
\r
9785 int width = get_term_width();
\r
9786 int len = wrap(NULL, message, count, width, &line);
\r
9787 char *msg = malloc(len);
\r
9791 return no_wrap(message, count);
\r
9794 dbgchk = wrap(msg, message, count, width, &line);
\r
9795 if (dbgchk != len && appData.debugMode)
\r
9796 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9797 ConsoleOutput(msg, len, FALSE);
\r
9804 if (ovl.hEvent == NULL) {
\r
9805 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9807 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9809 switch (cp->kind) {
\r
9812 outCount = send(cp->sock, message, count, 0);
\r
9813 if (outCount == SOCKET_ERROR) {
\r
9814 *outError = WSAGetLastError();
\r
9816 *outError = NO_ERROR;
\r
9821 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9822 &dOutCount, NULL)) {
\r
9823 *outError = NO_ERROR;
\r
9824 outCount = (int) dOutCount;
\r
9826 *outError = GetLastError();
\r
9831 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9832 &dOutCount, &ovl);
\r
9833 if (*outError == NO_ERROR) {
\r
9834 outCount = (int) dOutCount;
\r
9844 if(n != 0) Sleep(n);
\r
9848 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9851 /* Ignore delay, not implemented for WinBoard */
\r
9852 return OutputToProcess(pr, message, count, outError);
\r
9857 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9858 char *buf, int count, int error)
\r
9860 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9863 /* see wgamelist.c for Game List functions */
\r
9864 /* see wedittags.c for Edit Tags functions */
\r
9871 char buf[MSG_SIZ];
\r
9874 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9875 f = fopen(buf, "r");
\r
9877 ProcessICSInitScript(f);
\r
9887 StartAnalysisClock()
\r
9889 if (analysisTimerEvent) return;
\r
9890 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9891 (UINT) 2000, NULL);
\r
9895 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9897 highlightInfo.sq[0].x = fromX;
\r
9898 highlightInfo.sq[0].y = fromY;
\r
9899 highlightInfo.sq[1].x = toX;
\r
9900 highlightInfo.sq[1].y = toY;
\r
9906 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9907 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9911 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9913 premoveHighlightInfo.sq[0].x = fromX;
\r
9914 premoveHighlightInfo.sq[0].y = fromY;
\r
9915 premoveHighlightInfo.sq[1].x = toX;
\r
9916 premoveHighlightInfo.sq[1].y = toY;
\r
9920 ClearPremoveHighlights()
\r
9922 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9923 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9927 ShutDownFrontEnd()
\r
9929 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9930 DeleteClipboardTempFiles();
\r
9936 if (IsIconic(hwndMain))
\r
9937 ShowWindow(hwndMain, SW_RESTORE);
\r
9939 SetActiveWindow(hwndMain);
\r
9943 * Prototypes for animation support routines
\r
9945 static void ScreenSquare(int column, int row, POINT * pt);
\r
9946 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9947 POINT frames[], int * nFrames);
\r
9953 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9954 { // [HGM] atomic: animate blast wave
\r
9957 explodeInfo.fromX = fromX;
\r
9958 explodeInfo.fromY = fromY;
\r
9959 explodeInfo.toX = toX;
\r
9960 explodeInfo.toY = toY;
\r
9961 for(i=1; i<4*kFactor; i++) {
\r
9962 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9963 DrawPosition(FALSE, board);
\r
9964 Sleep(appData.animSpeed);
\r
9966 explodeInfo.radius = 0;
\r
9967 DrawPosition(TRUE, board);
\r
9971 AnimateMove(board, fromX, fromY, toX, toY)
\r
9978 ChessSquare piece;
\r
9979 int x = toX, y = toY;
\r
9980 POINT start, finish, mid;
\r
9981 POINT frames[kFactor * 2 + 1];
\r
9984 if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();
\r
9986 if (!appData.animate) return;
\r
9987 if (doingSizing) return;
\r
9988 if (fromY < 0 || fromX < 0) return;
\r
9989 piece = board[fromY][fromX];
\r
9990 if (piece >= EmptySquare) return;
\r
9992 if(killX >= 0) toX = killX, toY = killY; // [HGM] lion: first to kill square
\r
9996 ScreenSquare(fromX, fromY, &start);
\r
9997 ScreenSquare(toX, toY, &finish);
\r
9999 /* All moves except knight jumps move in straight line */
\r
10000 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
10001 mid.x = start.x + (finish.x - start.x) / 2;
\r
10002 mid.y = start.y + (finish.y - start.y) / 2;
\r
10004 /* Knight: make straight movement then diagonal */
\r
10005 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
10006 mid.x = start.x + (finish.x - start.x) / 2;
\r
10010 mid.y = start.y + (finish.y - start.y) / 2;
\r
10014 /* Don't use as many frames for very short moves */
\r
10015 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
10016 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
10018 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
10020 animInfo.from.x = fromX;
\r
10021 animInfo.from.y = fromY;
\r
10022 animInfo.to.x = toX;
\r
10023 animInfo.to.y = toY;
\r
10024 animInfo.lastpos = start;
\r
10025 animInfo.piece = piece;
\r
10026 for (n = 0; n < nFrames; n++) {
\r
10027 animInfo.pos = frames[n];
\r
10028 DrawPosition(FALSE, NULL);
\r
10029 animInfo.lastpos = animInfo.pos;
\r
10030 Sleep(appData.animSpeed);
\r
10032 animInfo.pos = finish;
\r
10033 DrawPosition(FALSE, NULL);
\r
10035 if(toX != x || toY != y) { fromX = toX; fromY = toY; toX = x; toY = y; goto again; } // second leg
\r
10037 animInfo.piece = EmptySquare;
\r
10038 Explode(board, fromX, fromY, toX, toY);
\r
10041 /* Convert board position to corner of screen rect and color */
\r
10044 ScreenSquare(column, row, pt)
\r
10045 int column; int row; POINT * pt;
\r
10048 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
10049 pt->y = lineGap + row * (squareSize + lineGap) + border;
\r
10051 pt->x = lineGap + column * (squareSize + lineGap) + border;
\r
10052 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
10056 /* Generate a series of frame coords from start->mid->finish.
\r
10057 The movement rate doubles until the half way point is
\r
10058 reached, then halves back down to the final destination,
\r
10059 which gives a nice slow in/out effect. The algorithmn
\r
10060 may seem to generate too many intermediates for short
\r
10061 moves, but remember that the purpose is to attract the
\r
10062 viewers attention to the piece about to be moved and
\r
10063 then to where it ends up. Too few frames would be less
\r
10067 Tween(start, mid, finish, factor, frames, nFrames)
\r
10068 POINT * start; POINT * mid;
\r
10069 POINT * finish; int factor;
\r
10070 POINT frames[]; int * nFrames;
\r
10072 int n, fraction = 1, count = 0;
\r
10074 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
10075 for (n = 0; n < factor; n++)
\r
10077 for (n = 0; n < factor; n++) {
\r
10078 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
10079 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
10081 fraction = fraction / 2;
\r
10085 frames[count] = *mid;
\r
10088 /* Slow out, stepping 1/2, then 1/4, ... */
\r
10090 for (n = 0; n < factor; n++) {
\r
10091 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
10092 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
10094 fraction = fraction * 2;
\r
10096 *nFrames = count;
\r
10100 SettingsPopUp(ChessProgramState *cps)
\r
10101 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
10102 EngineOptionsPopup(savedHwnd, cps);
\r
10105 int flock(int fid, int code)
\r
10107 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
10109 ov.hEvent = NULL;
\r
10111 ov.OffsetHigh = 0;
\r
10113 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
10115 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
10116 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
10117 default: return -1;
\r
10126 static char col[8][20];
\r
10127 COLORREF color = *(COLORREF *) colorVariable[n];
\r
10129 snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
10134 ActivateTheme (int new)
\r
10135 { // Redo initialization of features depending on options that can occur in themes
\r
10137 if(new) InitDrawingColors();
\r
10138 fontBitmapSquareSize = 0; // request creation of new font pieces
\r
10139 InitDrawingSizes(boardSize, 0);
\r
10140 InvalidateRect(hwndMain, NULL, TRUE);
\r