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
4515 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4516 /* Center the dialog over the application window */
\r
4517 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4518 Translate(hDlg, DLG_PromotionKing);
\r
4519 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4520 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4521 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4522 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4523 SW_SHOW : SW_HIDE);
\r
4524 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4525 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4526 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4527 PieceToChar(WhiteAngel) != '~') ||
\r
4528 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4529 PieceToChar(BlackAngel) != '~') ) ?
\r
4530 SW_SHOW : SW_HIDE);
\r
4531 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4532 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4533 PieceToChar(WhiteMarshall) != '~') ||
\r
4534 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4535 PieceToChar(BlackMarshall) != '~') ) ?
\r
4536 SW_SHOW : SW_HIDE);
\r
4537 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4538 ShowWindow(GetDlgItem(hDlg, PB_Rook), !promoStyle ? SW_SHOW : SW_HIDE);
\r
4539 ShowWindow(GetDlgItem(hDlg, PB_Bishop), !promoStyle ? SW_SHOW : SW_HIDE);
\r
4541 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4542 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4543 SetWindowText(hDlg, "Promote?");
\r
4545 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4546 gameInfo.variant == VariantSuper ?
\r
4547 SW_SHOW : SW_HIDE);
\r
4550 case WM_COMMAND: /* message: received a command */
\r
4551 switch (LOWORD(wParam)) {
\r
4553 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4554 ClearHighlights();
\r
4555 DrawPosition(FALSE, NULL);
\r
4558 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4561 promoChar = promoStyle ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4564 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4565 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4568 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4569 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4571 case PB_Chancellor:
\r
4572 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4574 case PB_Archbishop:
\r
4575 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4578 promoChar = gameInfo.variant == VariantShogi ? '=' : promoStyle ? NULLCHAR :
\r
4579 ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight));
\r
4584 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4585 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4586 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4587 fromX = fromY = -1;
\r
4588 if (!appData.highlightLastMove) {
\r
4589 ClearHighlights();
\r
4590 DrawPosition(FALSE, NULL);
\r
4597 /* Pop up promotion dialog */
\r
4599 PromotionPopup(HWND hwnd)
\r
4603 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4604 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4605 hwnd, (DLGPROC)lpProc);
\r
4606 FreeProcInstance(lpProc);
\r
4610 PromotionPopUp(char choice)
\r
4612 promoStyle = (choice == '+');
\r
4613 DrawPosition(TRUE, NULL);
\r
4614 PromotionPopup(hwndMain);
\r
4618 LoadGameDialog(HWND hwnd, char* title)
\r
4622 char fileTitle[MSG_SIZ];
\r
4623 f = OpenFileDialog(hwnd, "rb", "",
\r
4624 appData.oldSaveStyle ? "gam" : "pgn",
\r
4626 title, &number, fileTitle, NULL);
\r
4628 cmailMsgLoaded = FALSE;
\r
4629 if (number == 0) {
\r
4630 int error = GameListBuild(f);
\r
4632 DisplayError(_("Cannot build game list"), error);
\r
4633 } else if (!ListEmpty(&gameList) &&
\r
4634 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4635 GameListPopUp(f, fileTitle);
\r
4638 GameListDestroy();
\r
4641 LoadGame(f, number, fileTitle, FALSE);
\r
4645 int get_term_width()
\r
4650 HFONT hfont, hold_font;
\r
4655 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4659 // get the text metrics
\r
4660 hdc = GetDC(hText);
\r
4661 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4662 if (consoleCF.dwEffects & CFE_BOLD)
\r
4663 lf.lfWeight = FW_BOLD;
\r
4664 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4665 lf.lfItalic = TRUE;
\r
4666 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4667 lf.lfStrikeOut = TRUE;
\r
4668 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4669 lf.lfUnderline = TRUE;
\r
4670 hfont = CreateFontIndirect(&lf);
\r
4671 hold_font = SelectObject(hdc, hfont);
\r
4672 GetTextMetrics(hdc, &tm);
\r
4673 SelectObject(hdc, hold_font);
\r
4674 DeleteObject(hfont);
\r
4675 ReleaseDC(hText, hdc);
\r
4677 // get the rectangle
\r
4678 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4680 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4683 void UpdateICSWidth(HWND hText)
\r
4685 LONG old_width, new_width;
\r
4687 new_width = get_term_width(hText, FALSE);
\r
4688 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4689 if (new_width != old_width)
\r
4691 ics_update_width(new_width);
\r
4692 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4697 ChangedConsoleFont()
\r
4700 CHARRANGE tmpsel, sel;
\r
4701 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4702 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4703 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4706 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4707 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4708 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4709 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4710 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4711 * size. This was undocumented in the version of MSVC++ that I had
\r
4712 * when I wrote the code, but is apparently documented now.
\r
4714 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4715 cfmt.bCharSet = f->lf.lfCharSet;
\r
4716 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4717 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4718 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4719 /* Why are the following seemingly needed too? */
\r
4720 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4721 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4722 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4724 tmpsel.cpMax = -1; /*999999?*/
\r
4725 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4726 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4727 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4728 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4730 paraf.cbSize = sizeof(paraf);
\r
4731 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4732 paraf.dxStartIndent = 0;
\r
4733 paraf.dxOffset = WRAP_INDENT;
\r
4734 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4735 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4736 UpdateICSWidth(hText);
\r
4739 /*---------------------------------------------------------------------------*\
\r
4741 * Window Proc for main window
\r
4743 \*---------------------------------------------------------------------------*/
\r
4745 /* Process messages for main window, etc. */
\r
4747 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4754 char fileTitle[MSG_SIZ];
\r
4755 static SnapData sd;
\r
4756 static int peek=0;
\r
4758 switch (message) {
\r
4760 case WM_PAINT: /* message: repaint portion of window */
\r
4764 case WM_ERASEBKGND:
\r
4765 if (IsIconic(hwnd)) {
\r
4766 /* Cheat; change the message */
\r
4767 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4769 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4773 case WM_LBUTTONDOWN:
\r
4774 case WM_MBUTTONDOWN:
\r
4775 case WM_RBUTTONDOWN:
\r
4776 case WM_LBUTTONUP:
\r
4777 case WM_MBUTTONUP:
\r
4778 case WM_RBUTTONUP:
\r
4779 case WM_MOUSEMOVE:
\r
4780 case WM_MOUSEWHEEL:
\r
4781 MouseEvent(hwnd, message, wParam, lParam);
\r
4785 if((char)wParam == '\b') {
\r
4786 ForwardEvent(); peek = 0;
\r
4789 JAWS_KBUP_NAVIGATION
\r
4794 if((char)wParam == '\b') {
\r
4795 if(!peek) BackwardEvent(), peek = 1;
\r
4798 JAWS_KBDOWN_NAVIGATION
\r
4804 JAWS_ALT_INTERCEPT
\r
4806 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4807 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4808 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4809 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4811 SendMessage(h, message, wParam, lParam);
\r
4812 } else if(lParam != KF_REPEAT) {
\r
4813 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4814 TypeInEvent((char)wParam);
\r
4815 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4816 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4821 case WM_PALETTECHANGED:
\r
4822 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4824 HDC hdc = GetDC(hwndMain);
\r
4825 SelectPalette(hdc, hPal, TRUE);
\r
4826 nnew = RealizePalette(hdc);
\r
4828 paletteChanged = TRUE;
\r
4830 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4832 ReleaseDC(hwnd, hdc);
\r
4836 case WM_QUERYNEWPALETTE:
\r
4837 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4839 HDC hdc = GetDC(hwndMain);
\r
4840 paletteChanged = FALSE;
\r
4841 SelectPalette(hdc, hPal, FALSE);
\r
4842 nnew = RealizePalette(hdc);
\r
4844 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4846 ReleaseDC(hwnd, hdc);
\r
4851 case WM_COMMAND: /* message: command from application menu */
\r
4852 wmId = LOWORD(wParam);
\r
4857 SAY("new game enter a move to play against the computer with white");
\r
4860 case IDM_NewGameFRC:
\r
4861 if( NewGameFRC() == 0 ) {
\r
4866 case IDM_NewVariant:
\r
4867 NewVariantPopup(hwnd);
\r
4870 case IDM_LoadGame:
\r
4871 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4874 case IDM_LoadNextGame:
\r
4878 case IDM_LoadPrevGame:
\r
4882 case IDM_ReloadGame:
\r
4886 case IDM_LoadPosition:
\r
4887 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4888 Reset(FALSE, TRUE);
\r
4891 f = OpenFileDialog(hwnd, "rb", "",
\r
4892 appData.oldSaveStyle ? "pos" : "fen",
\r
4894 _("Load Position from File"), &number, fileTitle, NULL);
\r
4896 LoadPosition(f, number, fileTitle);
\r
4900 case IDM_LoadNextPosition:
\r
4901 ReloadPosition(1);
\r
4904 case IDM_LoadPrevPosition:
\r
4905 ReloadPosition(-1);
\r
4908 case IDM_ReloadPosition:
\r
4909 ReloadPosition(0);
\r
4912 case IDM_SaveGame:
\r
4913 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4914 f = OpenFileDialog(hwnd, "a", defName,
\r
4915 appData.oldSaveStyle ? "gam" : "pgn",
\r
4917 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4919 SaveGame(f, 0, "");
\r
4923 case IDM_SavePosition:
\r
4924 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4925 f = OpenFileDialog(hwnd, "a", defName,
\r
4926 appData.oldSaveStyle ? "pos" : "fen",
\r
4928 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4930 SavePosition(f, 0, "");
\r
4934 case IDM_SaveDiagram:
\r
4935 defName = "diagram";
\r
4936 f = OpenFileDialog(hwnd, "wb", defName,
\r
4939 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
4945 case IDM_SaveSelected:
\r
4946 f = OpenFileDialog(hwnd, "a", "",
\r
4949 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4951 SaveSelected(f, 0, "");
\r
4955 case IDM_CreateBook:
\r
4956 CreateBookEvent();
\r
4959 case IDM_CopyGame:
\r
4960 CopyGameToClipboard();
\r
4963 case IDM_PasteGame:
\r
4964 PasteGameFromClipboard();
\r
4967 case IDM_CopyGameListToClipboard:
\r
4968 CopyGameListToClipboard();
\r
4971 /* [AS] Autodetect FEN or PGN data */
\r
4972 case IDM_PasteAny:
\r
4973 PasteGameOrFENFromClipboard();
\r
4976 /* [AS] Move history */
\r
4977 case IDM_ShowMoveHistory:
\r
4978 if( MoveHistoryIsUp() ) {
\r
4979 MoveHistoryPopDown();
\r
4982 MoveHistoryPopUp();
\r
4986 /* [AS] Eval graph */
\r
4987 case IDM_ShowEvalGraph:
\r
4988 if( EvalGraphIsUp() ) {
\r
4989 EvalGraphPopDown();
\r
4993 SetFocus(hwndMain);
\r
4997 /* [AS] Engine output */
\r
4998 case IDM_ShowEngineOutput:
\r
4999 if( EngineOutputIsUp() ) {
\r
5000 EngineOutputPopDown();
\r
5003 EngineOutputPopUp();
\r
5007 /* [AS] User adjudication */
\r
5008 case IDM_UserAdjudication_White:
\r
5009 UserAdjudicationEvent( +1 );
\r
5012 case IDM_UserAdjudication_Black:
\r
5013 UserAdjudicationEvent( -1 );
\r
5016 case IDM_UserAdjudication_Draw:
\r
5017 UserAdjudicationEvent( 0 );
\r
5020 /* [AS] Game list options dialog */
\r
5021 case IDM_GameListOptions:
\r
5022 GameListOptions();
\r
5029 case IDM_CopyPosition:
\r
5030 CopyFENToClipboard();
\r
5033 case IDM_PastePosition:
\r
5034 PasteFENFromClipboard();
\r
5037 case IDM_MailMove:
\r
5041 case IDM_ReloadCMailMsg:
\r
5042 Reset(TRUE, TRUE);
\r
5043 ReloadCmailMsgEvent(FALSE);
\r
5046 case IDM_Minimize:
\r
5047 ShowWindow(hwnd, SW_MINIMIZE);
\r
5054 case IDM_MachineWhite:
\r
5055 MachineWhiteEvent();
\r
5057 * refresh the tags dialog only if it's visible
\r
5059 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
5061 tags = PGNTags(&gameInfo);
\r
5062 TagsPopUp(tags, CmailMsg());
\r
5065 SAY("computer starts playing white");
\r
5068 case IDM_MachineBlack:
\r
5069 MachineBlackEvent();
\r
5071 * refresh the tags dialog only if it's visible
\r
5073 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
5075 tags = PGNTags(&gameInfo);
\r
5076 TagsPopUp(tags, CmailMsg());
\r
5079 SAY("computer starts playing black");
\r
5082 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
5083 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
5086 case IDM_TwoMachines:
\r
5087 TwoMachinesEvent();
\r
5089 * refresh the tags dialog only if it's visible
\r
5091 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
5093 tags = PGNTags(&gameInfo);
\r
5094 TagsPopUp(tags, CmailMsg());
\r
5097 SAY("computer starts playing both sides");
\r
5100 case IDM_AnalysisMode:
\r
5101 if(AnalyzeModeEvent()) {
\r
5102 SAY("analyzing current position");
\r
5106 case IDM_AnalyzeFile:
\r
5107 AnalyzeFileEvent();
\r
5110 case IDM_IcsClient:
\r
5114 case IDM_EditGame:
\r
5115 case IDM_EditGame2:
\r
5120 case IDM_EditPosition:
\r
5121 case IDM_EditPosition2:
\r
5122 EditPositionEvent();
\r
5123 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
5126 case IDM_Training:
\r
5130 case IDM_ShowGameList:
\r
5131 ShowGameListProc();
\r
5134 case IDM_EditProgs1:
\r
5135 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
5138 case IDM_LoadProg1:
\r
5139 LoadEnginePopUp(hwndMain, 0);
\r
5142 case IDM_LoadProg2:
\r
5143 LoadEnginePopUp(hwndMain, 1);
\r
5146 case IDM_EditServers:
\r
5147 EditTagsPopUp(icsNames, &icsNames);
\r
5150 case IDM_EditTags:
\r
5155 case IDM_EditBook:
\r
5159 case IDM_EditComment:
\r
5161 if (commentUp && editComment) {
\r
5164 EditCommentEvent();
\r
5185 case IDM_CallFlag:
\r
5205 case IDM_StopObserving:
\r
5206 StopObservingEvent();
\r
5209 case IDM_StopExamining:
\r
5210 StopExaminingEvent();
\r
5214 UploadGameEvent();
\r
5217 case IDM_TypeInMove:
\r
5218 TypeInEvent('\000');
\r
5221 case IDM_TypeInName:
\r
5222 PopUpNameDialog('\000');
\r
5225 case IDM_Backward:
\r
5227 SetFocus(hwndMain);
\r
5234 SetFocus(hwndMain);
\r
5239 SetFocus(hwndMain);
\r
5244 SetFocus(hwndMain);
\r
5247 case OPT_GameListNext: // [HGM] forward these two accelerators to Game List
\r
5248 case OPT_GameListPrev:
\r
5249 if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);
\r
5253 RevertEvent(FALSE);
\r
5256 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5257 RevertEvent(TRUE);
\r
5260 case IDM_TruncateGame:
\r
5261 TruncateGameEvent();
\r
5268 case IDM_RetractMove:
\r
5269 RetractMoveEvent();
\r
5272 case IDM_FlipView:
\r
5273 flipView = !flipView;
\r
5274 DrawPosition(FALSE, NULL);
\r
5277 case IDM_FlipClock:
\r
5278 flipClock = !flipClock;
\r
5279 DisplayBothClocks();
\r
5283 case IDM_MuteSounds:
\r
5284 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5285 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5286 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5289 case IDM_GeneralOptions:
\r
5290 GeneralOptionsPopup(hwnd);
\r
5291 DrawPosition(TRUE, NULL);
\r
5294 case IDM_BoardOptions:
\r
5295 BoardOptionsPopup(hwnd);
\r
5298 case IDM_ThemeOptions:
\r
5299 ThemeOptionsPopup(hwnd);
\r
5302 case IDM_EnginePlayOptions:
\r
5303 EnginePlayOptionsPopup(hwnd);
\r
5306 case IDM_Engine1Options:
\r
5307 EngineOptionsPopup(hwnd, &first);
\r
5310 case IDM_Engine2Options:
\r
5312 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5313 EngineOptionsPopup(hwnd, &second);
\r
5316 case IDM_OptionsUCI:
\r
5317 UciOptionsPopup(hwnd);
\r
5321 TourneyPopup(hwnd);
\r
5324 case IDM_IcsOptions:
\r
5325 IcsOptionsPopup(hwnd);
\r
5329 FontsOptionsPopup(hwnd);
\r
5333 SoundOptionsPopup(hwnd);
\r
5336 case IDM_CommPort:
\r
5337 CommPortOptionsPopup(hwnd);
\r
5340 case IDM_LoadOptions:
\r
5341 LoadOptionsPopup(hwnd);
\r
5344 case IDM_SaveOptions:
\r
5345 SaveOptionsPopup(hwnd);
\r
5348 case IDM_TimeControl:
\r
5349 TimeControlOptionsPopup(hwnd);
\r
5352 case IDM_SaveSettings:
\r
5353 SaveSettings(settingsFileName);
\r
5356 case IDM_SaveSettingsOnExit:
\r
5357 saveSettingsOnExit = !saveSettingsOnExit;
\r
5358 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5359 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5360 MF_CHECKED : MF_UNCHECKED));
\r
5371 case IDM_AboutGame:
\r
5376 appData.debugMode = !appData.debugMode;
\r
5377 if (appData.debugMode) {
\r
5378 char dir[MSG_SIZ];
\r
5379 GetCurrentDirectory(MSG_SIZ, dir);
\r
5380 SetCurrentDirectory(installDir);
\r
5381 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5382 SetCurrentDirectory(dir);
\r
5383 setbuf(debugFP, NULL);
\r
5390 case IDM_HELPCONTENTS:
\r
5391 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5392 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5393 MessageBox (GetFocus(),
\r
5394 _("Unable to activate help"),
\r
5395 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5399 case IDM_HELPSEARCH:
\r
5400 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5401 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5402 MessageBox (GetFocus(),
\r
5403 _("Unable to activate help"),
\r
5404 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5408 case IDM_HELPHELP:
\r
5409 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5410 MessageBox (GetFocus(),
\r
5411 _("Unable to activate help"),
\r
5412 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5417 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5419 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5420 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5421 FreeProcInstance(lpProc);
\r
5424 case IDM_DirectCommand1:
\r
5425 AskQuestionEvent(_("Direct Command"),
\r
5426 _("Send to chess program:"), "", "1");
\r
5428 case IDM_DirectCommand2:
\r
5429 AskQuestionEvent(_("Direct Command"),
\r
5430 _("Send to second chess program:"), "", "2");
\r
5433 case EP_WhitePawn:
\r
5434 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5435 fromX = fromY = -1;
\r
5438 case EP_WhiteKnight:
\r
5439 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5440 fromX = fromY = -1;
\r
5443 case EP_WhiteBishop:
\r
5444 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5445 fromX = fromY = -1;
\r
5448 case EP_WhiteRook:
\r
5449 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5450 fromX = fromY = -1;
\r
5453 case EP_WhiteQueen:
\r
5454 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5455 fromX = fromY = -1;
\r
5458 case EP_WhiteFerz:
\r
5459 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5460 fromX = fromY = -1;
\r
5463 case EP_WhiteWazir:
\r
5464 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5465 fromX = fromY = -1;
\r
5468 case EP_WhiteAlfil:
\r
5469 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5470 fromX = fromY = -1;
\r
5473 case EP_WhiteCannon:
\r
5474 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5475 fromX = fromY = -1;
\r
5478 case EP_WhiteCardinal:
\r
5479 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5480 fromX = fromY = -1;
\r
5483 case EP_WhiteMarshall:
\r
5484 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5485 fromX = fromY = -1;
\r
5488 case EP_WhiteKing:
\r
5489 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5490 fromX = fromY = -1;
\r
5493 case EP_BlackPawn:
\r
5494 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5495 fromX = fromY = -1;
\r
5498 case EP_BlackKnight:
\r
5499 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5500 fromX = fromY = -1;
\r
5503 case EP_BlackBishop:
\r
5504 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5505 fromX = fromY = -1;
\r
5508 case EP_BlackRook:
\r
5509 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5510 fromX = fromY = -1;
\r
5513 case EP_BlackQueen:
\r
5514 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5515 fromX = fromY = -1;
\r
5518 case EP_BlackFerz:
\r
5519 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5520 fromX = fromY = -1;
\r
5523 case EP_BlackWazir:
\r
5524 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5525 fromX = fromY = -1;
\r
5528 case EP_BlackAlfil:
\r
5529 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5530 fromX = fromY = -1;
\r
5533 case EP_BlackCannon:
\r
5534 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5535 fromX = fromY = -1;
\r
5538 case EP_BlackCardinal:
\r
5539 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5540 fromX = fromY = -1;
\r
5543 case EP_BlackMarshall:
\r
5544 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5545 fromX = fromY = -1;
\r
5548 case EP_BlackKing:
\r
5549 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5550 fromX = fromY = -1;
\r
5553 case EP_EmptySquare:
\r
5554 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5555 fromX = fromY = -1;
\r
5558 case EP_ClearBoard:
\r
5559 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5560 fromX = fromY = -1;
\r
5564 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5565 fromX = fromY = -1;
\r
5569 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5570 fromX = fromY = -1;
\r
5574 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5575 fromX = fromY = -1;
\r
5579 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5580 fromX = fromY = -1;
\r
5584 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5585 fromX = fromY = -1;
\r
5589 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5590 fromX = fromY = -1;
\r
5594 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5595 fromX = fromY = -1;
\r
5599 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5600 fromX = fromY = -1;
\r
5604 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5605 fromX = fromY = -1;
\r
5609 barbaric = 0; appData.language = "";
\r
5610 TranslateMenus(0);
\r
5611 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5612 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5613 lastChecked = wmId;
\r
5617 if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)
\r
5618 RecentEngineEvent(wmId - IDM_RecentEngines);
\r
5620 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5621 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5622 TranslateMenus(0);
\r
5623 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5624 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5625 lastChecked = wmId;
\r
5628 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5634 case CLOCK_TIMER_ID:
\r
5635 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5636 clockTimerEvent = 0;
\r
5637 DecrementClocks(); /* call into back end */
\r
5639 case LOAD_GAME_TIMER_ID:
\r
5640 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5641 loadGameTimerEvent = 0;
\r
5642 AutoPlayGameLoop(); /* call into back end */
\r
5644 case ANALYSIS_TIMER_ID:
\r
5645 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5646 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5647 AnalysisPeriodicEvent(0);
\r
5649 KillTimer(hwnd, analysisTimerEvent);
\r
5650 analysisTimerEvent = 0;
\r
5653 case DELAYED_TIMER_ID:
\r
5654 KillTimer(hwnd, delayedTimerEvent);
\r
5655 delayedTimerEvent = 0;
\r
5656 delayedTimerCallback();
\r
5661 case WM_USER_Input:
\r
5662 InputEvent(hwnd, message, wParam, lParam);
\r
5665 /* [AS] Also move "attached" child windows */
\r
5666 case WM_WINDOWPOSCHANGING:
\r
5668 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5669 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5671 if( ((lpwp->flags & SWP_NOMOVE) == 0) /*&& ((lpwp->flags & SWP_NOSIZE) != 0)*/ ) { // [HGM] in Win8 size always accompanies move?
\r
5672 /* Window is moving */
\r
5675 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5676 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5677 rcMain.right = wpMain.x + wpMain.width;
\r
5678 rcMain.top = wpMain.y;
\r
5679 rcMain.bottom = wpMain.y + wpMain.height;
\r
5681 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5682 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5683 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5684 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5685 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5686 wpMain.x = lpwp->x;
\r
5687 wpMain.y = lpwp->y;
\r
5692 /* [AS] Snapping */
\r
5693 case WM_ENTERSIZEMOVE:
\r
5694 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5695 if (hwnd == hwndMain) {
\r
5696 doingSizing = TRUE;
\r
5699 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5703 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5704 if (hwnd == hwndMain) {
\r
5705 lastSizing = wParam;
\r
5710 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5711 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5713 case WM_EXITSIZEMOVE:
\r
5714 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5715 if (hwnd == hwndMain) {
\r
5717 doingSizing = FALSE;
\r
5718 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5719 GetClientRect(hwnd, &client);
\r
5720 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5722 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5724 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5727 case WM_DESTROY: /* message: window being destroyed */
\r
5728 PostQuitMessage(0);
\r
5732 if (hwnd == hwndMain) {
\r
5737 default: /* Passes it on if unprocessed */
\r
5738 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5743 /*---------------------------------------------------------------------------*\
\r
5745 * Misc utility routines
\r
5747 \*---------------------------------------------------------------------------*/
\r
5750 * Decent random number generator, at least not as bad as Windows
\r
5751 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5753 unsigned int randstate;
\r
5758 randstate = randstate * 1664525 + 1013904223;
\r
5759 return (int) randstate & 0x7fffffff;
\r
5763 mysrandom(unsigned int seed)
\r
5770 * returns TRUE if user selects a different color, FALSE otherwise
\r
5774 ChangeColor(HWND hwnd, COLORREF *which)
\r
5776 static BOOL firstTime = TRUE;
\r
5777 static DWORD customColors[16];
\r
5779 COLORREF newcolor;
\r
5784 /* Make initial colors in use available as custom colors */
\r
5785 /* Should we put the compiled-in defaults here instead? */
\r
5787 customColors[i++] = lightSquareColor & 0xffffff;
\r
5788 customColors[i++] = darkSquareColor & 0xffffff;
\r
5789 customColors[i++] = whitePieceColor & 0xffffff;
\r
5790 customColors[i++] = blackPieceColor & 0xffffff;
\r
5791 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5792 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5794 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5795 customColors[i++] = textAttribs[ccl].color;
\r
5797 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5798 firstTime = FALSE;
\r
5801 cc.lStructSize = sizeof(cc);
\r
5802 cc.hwndOwner = hwnd;
\r
5803 cc.hInstance = NULL;
\r
5804 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5805 cc.lpCustColors = (LPDWORD) customColors;
\r
5806 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5808 if (!ChooseColor(&cc)) return FALSE;
\r
5810 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5811 if (newcolor == *which) return FALSE;
\r
5812 *which = newcolor;
\r
5816 InitDrawingColors();
\r
5817 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5822 MyLoadSound(MySound *ms)
\r
5828 if (ms->data && ms->flag) free(ms->data);
\r
5831 switch (ms->name[0]) {
\r
5837 /* System sound from Control Panel. Don't preload here. */
\r
5841 if (ms->name[1] == NULLCHAR) {
\r
5842 /* "!" alone = silence */
\r
5845 /* Builtin wave resource. Error if not found. */
\r
5846 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5847 if (h == NULL) break;
\r
5848 ms->data = (void *)LoadResource(hInst, h);
\r
5849 ms->flag = 0; // not maloced, so cannot be freed!
\r
5850 if (h == NULL) break;
\r
5855 /* .wav file. Error if not found. */
\r
5856 f = fopen(ms->name, "rb");
\r
5857 if (f == NULL) break;
\r
5858 if (fstat(fileno(f), &st) < 0) break;
\r
5859 ms->data = malloc(st.st_size);
\r
5861 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5867 char buf[MSG_SIZ];
\r
5868 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5869 DisplayError(buf, GetLastError());
\r
5875 MyPlaySound(MySound *ms)
\r
5877 BOOLEAN ok = FALSE;
\r
5879 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5880 switch (ms->name[0]) {
\r
5882 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5887 /* System sound from Control Panel (deprecated feature).
\r
5888 "$" alone or an unset sound name gets default beep (still in use). */
\r
5889 if (ms->name[1]) {
\r
5890 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5892 if (!ok) ok = MessageBeep(MB_OK);
\r
5895 /* Builtin wave resource, or "!" alone for silence */
\r
5896 if (ms->name[1]) {
\r
5897 if (ms->data == NULL) return FALSE;
\r
5898 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5904 /* .wav file. Error if not found. */
\r
5905 if (ms->data == NULL) return FALSE;
\r
5906 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5909 /* Don't print an error: this can happen innocently if the sound driver
\r
5910 is busy; for instance, if another instance of WinBoard is playing
\r
5911 a sound at about the same time. */
\r
5917 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5920 OPENFILENAME *ofn;
\r
5921 static UINT *number; /* gross that this is static */
\r
5923 switch (message) {
\r
5924 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5925 /* Center the dialog over the application window */
\r
5926 ofn = (OPENFILENAME *) lParam;
\r
5927 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5928 number = (UINT *) ofn->lCustData;
\r
5929 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5933 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5934 Translate(hDlg, 1536);
\r
5935 return FALSE; /* Allow for further processing */
\r
5938 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5939 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5941 return FALSE; /* Allow for further processing */
\r
5947 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5949 static UINT *number;
\r
5950 OPENFILENAME *ofname;
\r
5953 case WM_INITDIALOG:
\r
5954 Translate(hdlg, DLG_IndexNumber);
\r
5955 ofname = (OPENFILENAME *)lParam;
\r
5956 number = (UINT *)(ofname->lCustData);
\r
5959 ofnot = (OFNOTIFY *)lParam;
\r
5960 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5961 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5970 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5971 char *nameFilt, char *dlgTitle, UINT *number,
\r
5972 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5974 OPENFILENAME openFileName;
\r
5975 char buf1[MSG_SIZ];
\r
5978 if (fileName == NULL) fileName = buf1;
\r
5979 if (defName == NULL) {
\r
5980 safeStrCpy(fileName, "*.", 3 );
\r
5981 strcat(fileName, defExt);
\r
5983 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5985 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5986 if (number) *number = 0;
\r
5988 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5989 openFileName.hwndOwner = hwnd;
\r
5990 openFileName.hInstance = (HANDLE) hInst;
\r
5991 openFileName.lpstrFilter = nameFilt;
\r
5992 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5993 openFileName.nMaxCustFilter = 0L;
\r
5994 openFileName.nFilterIndex = 1L;
\r
5995 openFileName.lpstrFile = fileName;
\r
5996 openFileName.nMaxFile = MSG_SIZ;
\r
5997 openFileName.lpstrFileTitle = fileTitle;
\r
5998 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5999 openFileName.lpstrInitialDir = NULL;
\r
6000 openFileName.lpstrTitle = dlgTitle;
\r
6001 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
6002 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
6003 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
6004 | (oldDialog ? 0 : OFN_EXPLORER);
\r
6005 openFileName.nFileOffset = 0;
\r
6006 openFileName.nFileExtension = 0;
\r
6007 openFileName.lpstrDefExt = defExt;
\r
6008 openFileName.lCustData = (LONG) number;
\r
6009 openFileName.lpfnHook = oldDialog ?
\r
6010 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
6011 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
6013 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
6014 GetOpenFileName(&openFileName)) {
\r
6015 /* open the file */
\r
6016 f = fopen(openFileName.lpstrFile, write);
\r
6018 MessageBox(hwnd, _("File open failed"), NULL,
\r
6019 MB_OK|MB_ICONEXCLAMATION);
\r
6023 int err = CommDlgExtendedError();
\r
6024 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
6033 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
6035 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
6038 * Get the first pop-up menu in the menu template. This is the
\r
6039 * menu that TrackPopupMenu displays.
\r
6041 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
6042 TranslateOneMenu(10, hmenuTrackPopup);
\r
6044 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
6047 * TrackPopup uses screen coordinates, so convert the
\r
6048 * coordinates of the mouse click to screen coordinates.
\r
6050 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
6052 /* Draw and track the floating pop-up menu. */
\r
6053 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
6054 pt.x, pt.y, 0, hwnd, NULL);
\r
6056 /* Destroy the menu.*/
\r
6057 DestroyMenu(hmenu);
\r
6062 int sizeX, sizeY, newSizeX, newSizeY;
\r
6064 } ResizeEditPlusButtonsClosure;
\r
6067 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
6069 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
6073 if (hChild == cl->hText) return TRUE;
\r
6074 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
6075 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
6076 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
6077 ScreenToClient(cl->hDlg, &pt);
\r
6078 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
6079 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
6083 /* Resize a dialog that has a (rich) edit field filling most of
\r
6084 the top, with a row of buttons below */
\r
6086 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
6089 int newTextHeight, newTextWidth;
\r
6090 ResizeEditPlusButtonsClosure cl;
\r
6092 /*if (IsIconic(hDlg)) return;*/
\r
6093 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
6095 cl.hdwp = BeginDeferWindowPos(8);
\r
6097 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
6098 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6099 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6100 if (newTextHeight < 0) {
\r
6101 newSizeY += -newTextHeight;
\r
6102 newTextHeight = 0;
\r
6104 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
6105 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6111 cl.newSizeX = newSizeX;
\r
6112 cl.newSizeY = newSizeY;
\r
6113 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
6115 EndDeferWindowPos(cl.hdwp);
\r
6118 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
6120 RECT rChild, rParent;
\r
6121 int wChild, hChild, wParent, hParent;
\r
6122 int wScreen, hScreen, xNew, yNew;
\r
6125 /* Get the Height and Width of the child window */
\r
6126 GetWindowRect (hwndChild, &rChild);
\r
6127 wChild = rChild.right - rChild.left;
\r
6128 hChild = rChild.bottom - rChild.top;
\r
6130 /* Get the Height and Width of the parent window */
\r
6131 GetWindowRect (hwndParent, &rParent);
\r
6132 wParent = rParent.right - rParent.left;
\r
6133 hParent = rParent.bottom - rParent.top;
\r
6135 /* Get the display limits */
\r
6136 hdc = GetDC (hwndChild);
\r
6137 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
6138 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
6139 ReleaseDC(hwndChild, hdc);
\r
6141 /* Calculate new X position, then adjust for screen */
\r
6142 xNew = rParent.left + ((wParent - wChild) /2);
\r
6145 } else if ((xNew+wChild) > wScreen) {
\r
6146 xNew = wScreen - wChild;
\r
6149 /* Calculate new Y position, then adjust for screen */
\r
6151 yNew = rParent.top + ((hParent - hChild) /2);
\r
6154 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
6159 } else if ((yNew+hChild) > hScreen) {
\r
6160 yNew = hScreen - hChild;
\r
6163 /* Set it, and return */
\r
6164 return SetWindowPos (hwndChild, NULL,
\r
6165 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
6168 /* Center one window over another */
\r
6169 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
6171 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
6174 /*---------------------------------------------------------------------------*\
\r
6176 * Startup Dialog functions
\r
6178 \*---------------------------------------------------------------------------*/
\r
6180 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
6182 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6184 while (*cd != NULL) {
\r
6185 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
6191 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
6193 char buf1[MAX_ARG_LEN];
\r
6196 if (str[0] == '@') {
\r
6197 FILE* f = fopen(str + 1, "r");
\r
6199 DisplayFatalError(str + 1, errno, 2);
\r
6202 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
6204 buf1[len] = NULLCHAR;
\r
6208 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6211 char buf[MSG_SIZ];
\r
6212 char *end = strchr(str, '\n');
\r
6213 if (end == NULL) return;
\r
6214 memcpy(buf, str, end - str);
\r
6215 buf[end - str] = NULLCHAR;
\r
6216 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6222 SetStartupDialogEnables(HWND hDlg)
\r
6224 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6225 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6226 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6227 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6228 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6229 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6230 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6231 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6232 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6233 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6234 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6235 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6236 IsDlgButtonChecked(hDlg, OPT_View));
\r
6240 QuoteForFilename(char *filename)
\r
6242 int dquote, space;
\r
6243 dquote = strchr(filename, '"') != NULL;
\r
6244 space = strchr(filename, ' ') != NULL;
\r
6245 if (dquote || space) {
\r
6257 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6259 char buf[MSG_SIZ];
\r
6262 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6263 q = QuoteForFilename(nthcp);
\r
6264 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6265 if (*nthdir != NULLCHAR) {
\r
6266 q = QuoteForFilename(nthdir);
\r
6267 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6269 if (*nthcp == NULLCHAR) {
\r
6270 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6271 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6272 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6273 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6278 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6280 char buf[MSG_SIZ];
\r
6284 switch (message) {
\r
6285 case WM_INITDIALOG:
\r
6286 /* Center the dialog */
\r
6287 CenterWindow (hDlg, GetDesktopWindow());
\r
6288 Translate(hDlg, DLG_Startup);
\r
6289 /* Initialize the dialog items */
\r
6290 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6291 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6292 firstChessProgramNames);
\r
6293 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6294 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6295 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6296 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6297 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6298 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6299 if (*appData.icsHelper != NULLCHAR) {
\r
6300 char *q = QuoteForFilename(appData.icsHelper);
\r
6301 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6303 if (*appData.icsHost == NULLCHAR) {
\r
6304 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6305 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6306 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6307 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6308 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6311 if (appData.icsActive) {
\r
6312 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6314 else if (appData.noChessProgram) {
\r
6315 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6318 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6321 SetStartupDialogEnables(hDlg);
\r
6325 switch (LOWORD(wParam)) {
\r
6327 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6328 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6329 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6331 comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox
\r
6332 ParseArgs(StringGet, &p);
\r
6333 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6334 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6336 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6337 ParseArgs(StringGet, &p);
\r
6338 SwapEngines(singleList); // ... and then make it 'second'
\r
6340 appData.noChessProgram = FALSE;
\r
6341 appData.icsActive = FALSE;
\r
6342 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6343 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6344 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6346 ParseArgs(StringGet, &p);
\r
6347 if (appData.zippyPlay) {
\r
6348 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6349 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6351 ParseArgs(StringGet, &p);
\r
6353 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6354 appData.noChessProgram = TRUE;
\r
6355 appData.icsActive = FALSE;
\r
6357 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6358 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6361 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6362 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6364 ParseArgs(StringGet, &p);
\r
6366 EndDialog(hDlg, TRUE);
\r
6373 case IDM_HELPCONTENTS:
\r
6374 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6375 MessageBox (GetFocus(),
\r
6376 _("Unable to activate help"),
\r
6377 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6382 SetStartupDialogEnables(hDlg);
\r
6390 /*---------------------------------------------------------------------------*\
\r
6392 * About box dialog functions
\r
6394 \*---------------------------------------------------------------------------*/
\r
6396 /* Process messages for "About" dialog box */
\r
6398 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6400 switch (message) {
\r
6401 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6402 /* Center the dialog over the application window */
\r
6403 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6404 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6405 Translate(hDlg, ABOUTBOX);
\r
6409 case WM_COMMAND: /* message: received a command */
\r
6410 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6411 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6412 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6420 /*---------------------------------------------------------------------------*\
\r
6422 * Comment Dialog functions
\r
6424 \*---------------------------------------------------------------------------*/
\r
6427 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6429 static HANDLE hwndText = NULL;
\r
6430 int len, newSizeX, newSizeY;
\r
6431 static int sizeX, sizeY;
\r
6436 switch (message) {
\r
6437 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6438 /* Initialize the dialog items */
\r
6439 Translate(hDlg, DLG_EditComment);
\r
6440 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6441 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6442 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6443 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6444 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6445 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6446 SetWindowText(hDlg, commentTitle);
\r
6447 if (editComment) {
\r
6448 SetFocus(hwndText);
\r
6450 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6452 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6453 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6454 MAKELPARAM(FALSE, 0));
\r
6455 /* Size and position the dialog */
\r
6456 if (!commentDialog) {
\r
6457 commentDialog = hDlg;
\r
6458 GetClientRect(hDlg, &rect);
\r
6459 sizeX = rect.right;
\r
6460 sizeY = rect.bottom;
\r
6461 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6462 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6463 WINDOWPLACEMENT wp;
\r
6464 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6465 wp.length = sizeof(WINDOWPLACEMENT);
\r
6467 wp.showCmd = SW_SHOW;
\r
6468 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6469 wp.rcNormalPosition.left = wpComment.x;
\r
6470 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6471 wp.rcNormalPosition.top = wpComment.y;
\r
6472 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6473 SetWindowPlacement(hDlg, &wp);
\r
6475 GetClientRect(hDlg, &rect);
\r
6476 newSizeX = rect.right;
\r
6477 newSizeY = rect.bottom;
\r
6478 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6479 newSizeX, newSizeY);
\r
6484 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6487 case WM_COMMAND: /* message: received a command */
\r
6488 switch (LOWORD(wParam)) {
\r
6490 if (editComment) {
\r
6492 /* Read changed options from the dialog box */
\r
6493 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6494 len = GetWindowTextLength(hwndText);
\r
6495 str = (char *) malloc(len + 1);
\r
6496 GetWindowText(hwndText, str, len + 1);
\r
6505 ReplaceComment(commentIndex, str);
\r
6512 case OPT_CancelComment:
\r
6516 case OPT_ClearComment:
\r
6517 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6520 case OPT_EditComment:
\r
6521 EditCommentEvent();
\r
6529 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6530 if( wParam == OPT_CommentText ) {
\r
6531 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6533 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6534 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6538 pt.x = LOWORD( lpMF->lParam );
\r
6539 pt.y = HIWORD( lpMF->lParam );
\r
6541 if(lpMF->msg == WM_CHAR) {
\r
6543 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6544 index = sel.cpMin;
\r
6546 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6548 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6549 len = GetWindowTextLength(hwndText);
\r
6550 str = (char *) malloc(len + 1);
\r
6551 GetWindowText(hwndText, str, len + 1);
\r
6552 ReplaceComment(commentIndex, str);
\r
6553 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6554 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6557 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6558 lpMF->msg = WM_USER;
\r
6566 newSizeX = LOWORD(lParam);
\r
6567 newSizeY = HIWORD(lParam);
\r
6568 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6573 case WM_GETMINMAXINFO:
\r
6574 /* Prevent resizing window too small */
\r
6575 mmi = (MINMAXINFO *) lParam;
\r
6576 mmi->ptMinTrackSize.x = 100;
\r
6577 mmi->ptMinTrackSize.y = 100;
\r
6584 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6589 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6591 if (str == NULL) str = "";
\r
6592 p = (char *) malloc(2 * strlen(str) + 2);
\r
6595 if (*str == '\n') *q++ = '\r';
\r
6599 if (commentText != NULL) free(commentText);
\r
6601 commentIndex = index;
\r
6602 commentTitle = title;
\r
6604 editComment = edit;
\r
6606 if (commentDialog) {
\r
6607 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6608 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6610 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6611 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6612 hwndMain, (DLGPROC)lpProc);
\r
6613 FreeProcInstance(lpProc);
\r
6619 /*---------------------------------------------------------------------------*\
\r
6621 * Type-in move dialog functions
\r
6623 \*---------------------------------------------------------------------------*/
\r
6626 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6628 char move[MSG_SIZ];
\r
6631 switch (message) {
\r
6632 case WM_INITDIALOG:
\r
6633 move[0] = (char) lParam;
\r
6634 move[1] = NULLCHAR;
\r
6635 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6636 Translate(hDlg, DLG_TypeInMove);
\r
6637 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6638 SetWindowText(hInput, move);
\r
6640 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6644 switch (LOWORD(wParam)) {
\r
6647 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6648 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6649 TypeInDoneEvent(move);
\r
6650 EndDialog(hDlg, TRUE);
\r
6653 EndDialog(hDlg, FALSE);
\r
6664 PopUpMoveDialog(char firstchar)
\r
6668 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6669 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6670 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6671 FreeProcInstance(lpProc);
\r
6674 /*---------------------------------------------------------------------------*\
\r
6676 * Type-in name dialog functions
\r
6678 \*---------------------------------------------------------------------------*/
\r
6681 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6683 char move[MSG_SIZ];
\r
6686 switch (message) {
\r
6687 case WM_INITDIALOG:
\r
6688 move[0] = (char) lParam;
\r
6689 move[1] = NULLCHAR;
\r
6690 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6691 Translate(hDlg, DLG_TypeInName);
\r
6692 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6693 SetWindowText(hInput, move);
\r
6695 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6699 switch (LOWORD(wParam)) {
\r
6701 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6702 appData.userName = strdup(move);
\r
6705 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6706 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6707 DisplayTitle(move);
\r
6711 EndDialog(hDlg, TRUE);
\r
6714 EndDialog(hDlg, FALSE);
\r
6725 PopUpNameDialog(char firstchar)
\r
6729 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6730 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6731 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6732 FreeProcInstance(lpProc);
\r
6735 /*---------------------------------------------------------------------------*\
\r
6739 \*---------------------------------------------------------------------------*/
\r
6741 /* Nonmodal error box */
\r
6742 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6743 WPARAM wParam, LPARAM lParam);
\r
6746 ErrorPopUp(char *title, char *content)
\r
6750 BOOLEAN modal = hwndMain == NULL;
\r
6768 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6769 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6772 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6774 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6775 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6776 hwndMain, (DLGPROC)lpProc);
\r
6777 FreeProcInstance(lpProc);
\r
6784 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6785 if (errorDialog == NULL) return;
\r
6786 DestroyWindow(errorDialog);
\r
6787 errorDialog = NULL;
\r
6788 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6792 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6796 switch (message) {
\r
6797 case WM_INITDIALOG:
\r
6798 GetWindowRect(hDlg, &rChild);
\r
6801 SetWindowPos(hDlg, NULL, rChild.left,
\r
6802 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6803 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6807 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6808 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6809 and it doesn't work when you resize the dialog.
\r
6810 For now, just give it a default position.
\r
6812 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6813 Translate(hDlg, DLG_Error);
\r
6815 errorDialog = hDlg;
\r
6816 SetWindowText(hDlg, errorTitle);
\r
6817 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6821 switch (LOWORD(wParam)) {
\r
6824 if (errorDialog == hDlg) errorDialog = NULL;
\r
6825 DestroyWindow(hDlg);
\r
6837 HWND gothicDialog = NULL;
\r
6840 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6843 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6845 switch (message) {
\r
6846 case WM_INITDIALOG:
\r
6847 GetWindowRect(hDlg, &rChild);
\r
6849 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6853 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6854 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6855 and it doesn't work when you resize the dialog.
\r
6856 For now, just give it a default position.
\r
6858 gothicDialog = hDlg;
\r
6859 SetWindowText(hDlg, errorTitle);
\r
6860 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6864 switch (LOWORD(wParam)) {
\r
6867 if (errorDialog == hDlg) errorDialog = NULL;
\r
6868 DestroyWindow(hDlg);
\r
6880 GothicPopUp(char *title, VariantClass variant)
\r
6883 static char *lastTitle;
\r
6885 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6886 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6888 if(lastTitle != title && gothicDialog != NULL) {
\r
6889 DestroyWindow(gothicDialog);
\r
6890 gothicDialog = NULL;
\r
6892 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6893 title = lastTitle;
\r
6894 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6895 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6896 hwndMain, (DLGPROC)lpProc);
\r
6897 FreeProcInstance(lpProc);
\r
6902 /*---------------------------------------------------------------------------*\
\r
6904 * Ics Interaction console functions
\r
6906 \*---------------------------------------------------------------------------*/
\r
6908 #define HISTORY_SIZE 64
\r
6909 static char *history[HISTORY_SIZE];
\r
6910 int histIn = 0, histP = 0;
\r
6914 SaveInHistory(char *cmd)
\r
6916 if (history[histIn] != NULL) {
\r
6917 free(history[histIn]);
\r
6918 history[histIn] = NULL;
\r
6920 if (*cmd == NULLCHAR) return;
\r
6921 history[histIn] = StrSave(cmd);
\r
6922 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6923 if (history[histIn] != NULL) {
\r
6924 free(history[histIn]);
\r
6926 history[histIn] = NULL;
\r
6932 PrevInHistory(char *cmd)
\r
6935 if (histP == histIn) {
\r
6936 if (history[histIn] != NULL) free(history[histIn]);
\r
6937 history[histIn] = StrSave(cmd);
\r
6939 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6940 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6942 return history[histP];
\r
6948 if (histP == histIn) return NULL;
\r
6949 histP = (histP + 1) % HISTORY_SIZE;
\r
6950 return history[histP];
\r
6954 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6958 hmenu = LoadMenu(hInst, "TextMenu");
\r
6959 h = GetSubMenu(hmenu, 0);
\r
6961 if (strcmp(e->item, "-") == 0) {
\r
6962 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6963 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6964 int flags = MF_STRING, j = 0;
\r
6965 if (e->item[0] == '|') {
\r
6966 flags |= MF_MENUBARBREAK;
\r
6969 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6970 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6978 WNDPROC consoleTextWindowProc;
\r
6981 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6983 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6984 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6988 SetWindowText(hInput, command);
\r
6990 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6992 sel.cpMin = 999999;
\r
6993 sel.cpMax = 999999;
\r
6994 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6999 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7000 if (sel.cpMin == sel.cpMax) {
\r
7001 /* Expand to surrounding word */
\r
7004 tr.chrg.cpMax = sel.cpMin;
\r
7005 tr.chrg.cpMin = --sel.cpMin;
\r
7006 if (sel.cpMin < 0) break;
\r
7007 tr.lpstrText = name;
\r
7008 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
7009 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
7013 tr.chrg.cpMin = sel.cpMax;
\r
7014 tr.chrg.cpMax = ++sel.cpMax;
\r
7015 tr.lpstrText = name;
\r
7016 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
7017 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
7020 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
7021 MessageBeep(MB_ICONEXCLAMATION);
\r
7025 tr.lpstrText = name;
\r
7026 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
7028 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
7029 MessageBeep(MB_ICONEXCLAMATION);
\r
7032 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
7035 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
7036 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
7037 SetWindowText(hInput, buf);
\r
7038 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
7040 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
7041 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
7042 SetWindowText(hInput, buf);
\r
7043 sel.cpMin = 999999;
\r
7044 sel.cpMax = 999999;
\r
7045 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7051 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7056 switch (message) {
\r
7058 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7059 if(wParam=='R') return 0;
\r
7062 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
7065 sel.cpMin = 999999;
\r
7066 sel.cpMax = 999999;
\r
7067 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7068 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
7073 if(wParam != '\022') {
\r
7074 if (wParam == '\t') {
\r
7075 if (GetKeyState(VK_SHIFT) < 0) {
\r
7077 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7078 if (buttonDesc[0].hwnd) {
\r
7079 SetFocus(buttonDesc[0].hwnd);
\r
7081 SetFocus(hwndMain);
\r
7085 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
7088 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7089 JAWS_DELETE( SetFocus(hInput); )
\r
7090 SendMessage(hInput, message, wParam, lParam);
\r
7093 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
7095 case WM_RBUTTONDOWN:
\r
7096 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
7097 /* Move selection here if it was empty */
\r
7099 pt.x = LOWORD(lParam);
\r
7100 pt.y = HIWORD(lParam);
\r
7101 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7102 if (sel.cpMin == sel.cpMax) {
\r
7103 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
7104 sel.cpMax = sel.cpMin;
\r
7105 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7107 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
7108 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
7110 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
7111 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7112 if (sel.cpMin == sel.cpMax) {
\r
7113 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7114 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
7116 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7117 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7119 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
7120 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
7121 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
7122 MenuPopup(hwnd, pt, hmenu, -1);
\r
7126 case WM_RBUTTONUP:
\r
7127 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7128 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7129 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7133 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7135 return SendMessage(hInput, message, wParam, lParam);
\r
7136 case WM_MBUTTONDOWN:
\r
7137 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7139 switch (LOWORD(wParam)) {
\r
7140 case IDM_QuickPaste:
\r
7142 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7143 if (sel.cpMin == sel.cpMax) {
\r
7144 MessageBeep(MB_ICONEXCLAMATION);
\r
7147 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7148 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7149 SendMessage(hInput, WM_PASTE, 0, 0);
\r
7154 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7157 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7160 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7164 int i = LOWORD(wParam) - IDM_CommandX;
\r
7165 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
7166 icsTextMenuEntry[i].command != NULL) {
\r
7167 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
7168 icsTextMenuEntry[i].getname,
\r
7169 icsTextMenuEntry[i].immediate);
\r
7177 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
7180 WNDPROC consoleInputWindowProc;
\r
7183 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7185 char buf[MSG_SIZ];
\r
7187 static BOOL sendNextChar = FALSE;
\r
7188 static BOOL quoteNextChar = FALSE;
\r
7189 InputSource *is = consoleInputSource;
\r
7193 switch (message) {
\r
7195 if (!appData.localLineEditing || sendNextChar) {
\r
7196 is->buf[0] = (CHAR) wParam;
\r
7198 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7199 sendNextChar = FALSE;
\r
7202 if (quoteNextChar) {
\r
7203 buf[0] = (char) wParam;
\r
7204 buf[1] = NULLCHAR;
\r
7205 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7206 quoteNextChar = FALSE;
\r
7210 case '\r': /* Enter key */
\r
7211 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7212 if (consoleEcho) SaveInHistory(is->buf);
\r
7213 is->buf[is->count++] = '\n';
\r
7214 is->buf[is->count] = NULLCHAR;
\r
7215 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7216 if (consoleEcho) {
\r
7217 ConsoleOutput(is->buf, is->count, TRUE);
\r
7218 } else if (appData.localLineEditing) {
\r
7219 ConsoleOutput("\n", 1, TRUE);
\r
7222 case '\033': /* Escape key */
\r
7223 SetWindowText(hwnd, "");
\r
7224 cf.cbSize = sizeof(CHARFORMAT);
\r
7225 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7226 if (consoleEcho) {
\r
7227 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7229 cf.crTextColor = COLOR_ECHOOFF;
\r
7231 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7232 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7234 case '\t': /* Tab key */
\r
7235 if (GetKeyState(VK_SHIFT) < 0) {
\r
7237 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7240 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7241 if (buttonDesc[0].hwnd) {
\r
7242 SetFocus(buttonDesc[0].hwnd);
\r
7244 SetFocus(hwndMain);
\r
7248 case '\023': /* Ctrl+S */
\r
7249 sendNextChar = TRUE;
\r
7251 case '\021': /* Ctrl+Q */
\r
7252 quoteNextChar = TRUE;
\r
7262 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7263 p = PrevInHistory(buf);
\r
7265 SetWindowText(hwnd, p);
\r
7266 sel.cpMin = 999999;
\r
7267 sel.cpMax = 999999;
\r
7268 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7273 p = NextInHistory();
\r
7275 SetWindowText(hwnd, p);
\r
7276 sel.cpMin = 999999;
\r
7277 sel.cpMax = 999999;
\r
7278 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7284 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7288 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7292 case WM_MBUTTONDOWN:
\r
7293 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7294 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7296 case WM_RBUTTONUP:
\r
7297 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7298 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7299 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7303 hmenu = LoadMenu(hInst, "InputMenu");
\r
7304 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7305 if (sel.cpMin == sel.cpMax) {
\r
7306 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7307 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7309 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7310 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7312 pt.x = LOWORD(lParam);
\r
7313 pt.y = HIWORD(lParam);
\r
7314 MenuPopup(hwnd, pt, hmenu, -1);
\r
7318 switch (LOWORD(wParam)) {
\r
7320 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7322 case IDM_SelectAll:
\r
7324 sel.cpMax = -1; /*999999?*/
\r
7325 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7328 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7331 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7334 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7339 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7342 #define CO_MAX 100000
\r
7343 #define CO_TRIM 1000
\r
7346 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7348 static SnapData sd;
\r
7349 HWND hText, hInput;
\r
7351 static int sizeX, sizeY;
\r
7352 int newSizeX, newSizeY;
\r
7356 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7357 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7359 switch (message) {
\r
7361 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7363 ENLINK *pLink = (ENLINK*)lParam;
\r
7364 if (pLink->msg == WM_LBUTTONUP)
\r
7368 tr.chrg = pLink->chrg;
\r
7369 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7370 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7371 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7372 free(tr.lpstrText);
\r
7376 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7377 hwndConsole = hDlg;
\r
7379 consoleTextWindowProc = (WNDPROC)
\r
7380 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7381 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7382 consoleInputWindowProc = (WNDPROC)
\r
7383 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7384 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7385 Colorize(ColorNormal, TRUE);
\r
7386 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7387 ChangedConsoleFont();
\r
7388 GetClientRect(hDlg, &rect);
\r
7389 sizeX = rect.right;
\r
7390 sizeY = rect.bottom;
\r
7391 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7392 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7393 WINDOWPLACEMENT wp;
\r
7394 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7395 wp.length = sizeof(WINDOWPLACEMENT);
\r
7397 wp.showCmd = SW_SHOW;
\r
7398 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7399 wp.rcNormalPosition.left = wpConsole.x;
\r
7400 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7401 wp.rcNormalPosition.top = wpConsole.y;
\r
7402 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7403 SetWindowPlacement(hDlg, &wp);
\r
7406 // [HGM] Chessknight's change 2004-07-13
\r
7407 else { /* Determine Defaults */
\r
7408 WINDOWPLACEMENT wp;
\r
7409 wpConsole.x = wpMain.width + 1;
\r
7410 wpConsole.y = wpMain.y;
\r
7411 wpConsole.width = screenWidth - wpMain.width;
\r
7412 wpConsole.height = wpMain.height;
\r
7413 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7414 wp.length = sizeof(WINDOWPLACEMENT);
\r
7416 wp.showCmd = SW_SHOW;
\r
7417 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7418 wp.rcNormalPosition.left = wpConsole.x;
\r
7419 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7420 wp.rcNormalPosition.top = wpConsole.y;
\r
7421 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7422 SetWindowPlacement(hDlg, &wp);
\r
7425 // Allow hText to highlight URLs and send notifications on them
\r
7426 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7427 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7428 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7429 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7443 if (IsIconic(hDlg)) break;
\r
7444 newSizeX = LOWORD(lParam);
\r
7445 newSizeY = HIWORD(lParam);
\r
7446 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7447 RECT rectText, rectInput;
\r
7449 int newTextHeight, newTextWidth;
\r
7450 GetWindowRect(hText, &rectText);
\r
7451 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7452 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7453 if (newTextHeight < 0) {
\r
7454 newSizeY += -newTextHeight;
\r
7455 newTextHeight = 0;
\r
7457 SetWindowPos(hText, NULL, 0, 0,
\r
7458 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7459 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7460 pt.x = rectInput.left;
\r
7461 pt.y = rectInput.top + newSizeY - sizeY;
\r
7462 ScreenToClient(hDlg, &pt);
\r
7463 SetWindowPos(hInput, NULL,
\r
7464 pt.x, pt.y, /* needs client coords */
\r
7465 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7466 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7472 case WM_GETMINMAXINFO:
\r
7473 /* Prevent resizing window too small */
\r
7474 mmi = (MINMAXINFO *) lParam;
\r
7475 mmi->ptMinTrackSize.x = 100;
\r
7476 mmi->ptMinTrackSize.y = 100;
\r
7479 /* [AS] Snapping */
\r
7480 case WM_ENTERSIZEMOVE:
\r
7481 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7484 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7487 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7489 case WM_EXITSIZEMOVE:
\r
7490 UpdateICSWidth(hText);
\r
7491 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7494 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7502 if (hwndConsole) return;
\r
7503 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7504 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7509 ConsoleOutput(char* data, int length, int forceVisible)
\r
7514 char buf[CO_MAX+1];
\r
7517 static int delayLF = 0;
\r
7518 CHARRANGE savesel, sel;
\r
7520 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7528 while (length--) {
\r
7536 } else if (*p == '\007') {
\r
7537 MyPlaySound(&sounds[(int)SoundBell]);
\r
7544 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7545 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7546 /* Save current selection */
\r
7547 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7548 exlen = GetWindowTextLength(hText);
\r
7549 /* Find out whether current end of text is visible */
\r
7550 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7551 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7552 /* Trim existing text if it's too long */
\r
7553 if (exlen + (q - buf) > CO_MAX) {
\r
7554 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7557 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7558 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7560 savesel.cpMin -= trim;
\r
7561 savesel.cpMax -= trim;
\r
7562 if (exlen < 0) exlen = 0;
\r
7563 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7564 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7566 /* Append the new text */
\r
7567 sel.cpMin = exlen;
\r
7568 sel.cpMax = exlen;
\r
7569 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7570 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7571 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7572 if (forceVisible || exlen == 0 ||
\r
7573 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7574 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7575 /* Scroll to make new end of text visible if old end of text
\r
7576 was visible or new text is an echo of user typein */
\r
7577 sel.cpMin = 9999999;
\r
7578 sel.cpMax = 9999999;
\r
7579 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7580 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7581 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7582 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7584 if (savesel.cpMax == exlen || forceVisible) {
\r
7585 /* Move insert point to new end of text if it was at the old
\r
7586 end of text or if the new text is an echo of user typein */
\r
7587 sel.cpMin = 9999999;
\r
7588 sel.cpMax = 9999999;
\r
7589 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7591 /* Restore previous selection */
\r
7592 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7594 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7601 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7605 COLORREF oldFg, oldBg;
\r
7609 if(copyNumber > 1)
\r
7610 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7612 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7613 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7614 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7617 rect.right = x + squareSize;
\r
7619 rect.bottom = y + squareSize;
\r
7622 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7623 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7624 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7625 &rect, str, strlen(str), NULL);
\r
7627 (void) SetTextColor(hdc, oldFg);
\r
7628 (void) SetBkColor(hdc, oldBg);
\r
7629 (void) SelectObject(hdc, oldFont);
\r
7633 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7634 RECT *rect, char *color, char *flagFell)
\r
7638 COLORREF oldFg, oldBg;
\r
7641 if (twoBoards && partnerUp) return;
\r
7642 if (appData.clockMode) {
\r
7644 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7646 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7653 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7654 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7656 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7657 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7659 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7663 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7664 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7665 rect, str, strlen(str), NULL);
\r
7666 if(logoHeight > 0 && appData.clockMode) {
\r
7668 str += strlen(color)+2;
\r
7669 r.top = rect->top + logoHeight/2;
\r
7670 r.left = rect->left;
\r
7671 r.right = rect->right;
\r
7672 r.bottom = rect->bottom;
\r
7673 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7674 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7675 &r, str, strlen(str), NULL);
\r
7677 (void) SetTextColor(hdc, oldFg);
\r
7678 (void) SetBkColor(hdc, oldBg);
\r
7679 (void) SelectObject(hdc, oldFont);
\r
7684 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7690 if( count <= 0 ) {
\r
7691 if (appData.debugMode) {
\r
7692 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7695 return ERROR_INVALID_USER_BUFFER;
\r
7698 ResetEvent(ovl->hEvent);
\r
7699 ovl->Offset = ovl->OffsetHigh = 0;
\r
7700 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7704 err = GetLastError();
\r
7705 if (err == ERROR_IO_PENDING) {
\r
7706 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7710 err = GetLastError();
\r
7717 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7722 ResetEvent(ovl->hEvent);
\r
7723 ovl->Offset = ovl->OffsetHigh = 0;
\r
7724 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7728 err = GetLastError();
\r
7729 if (err == ERROR_IO_PENDING) {
\r
7730 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7734 err = GetLastError();
\r
7741 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7742 void CheckForInputBufferFull( InputSource * is )
\r
7744 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7745 /* Look for end of line */
\r
7746 char * p = is->buf;
\r
7748 while( p < is->next && *p != '\n' ) {
\r
7752 if( p >= is->next ) {
\r
7753 if (appData.debugMode) {
\r
7754 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7757 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7758 is->count = (DWORD) -1;
\r
7759 is->next = is->buf;
\r
7765 InputThread(LPVOID arg)
\r
7770 is = (InputSource *) arg;
\r
7771 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7772 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7773 while (is->hThread != NULL) {
\r
7774 is->error = DoReadFile(is->hFile, is->next,
\r
7775 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7776 &is->count, &ovl);
\r
7777 if (is->error == NO_ERROR) {
\r
7778 is->next += is->count;
\r
7780 if (is->error == ERROR_BROKEN_PIPE) {
\r
7781 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7784 is->count = (DWORD) -1;
\r
7785 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7790 CheckForInputBufferFull( is );
\r
7792 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7794 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7796 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7799 CloseHandle(ovl.hEvent);
\r
7800 CloseHandle(is->hFile);
\r
7802 if (appData.debugMode) {
\r
7803 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7810 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7812 NonOvlInputThread(LPVOID arg)
\r
7819 is = (InputSource *) arg;
\r
7820 while (is->hThread != NULL) {
\r
7821 is->error = ReadFile(is->hFile, is->next,
\r
7822 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7823 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7824 if (is->error == NO_ERROR) {
\r
7825 /* Change CRLF to LF */
\r
7826 if (is->next > is->buf) {
\r
7828 i = is->count + 1;
\r
7836 if (prev == '\r' && *p == '\n') {
\r
7848 if (is->error == ERROR_BROKEN_PIPE) {
\r
7849 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7852 is->count = (DWORD) -1;
\r
7856 CheckForInputBufferFull( is );
\r
7858 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7860 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7862 if (is->count < 0) break; /* Quit on error */
\r
7864 CloseHandle(is->hFile);
\r
7869 SocketInputThread(LPVOID arg)
\r
7873 is = (InputSource *) arg;
\r
7874 while (is->hThread != NULL) {
\r
7875 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7876 if ((int)is->count == SOCKET_ERROR) {
\r
7877 is->count = (DWORD) -1;
\r
7878 is->error = WSAGetLastError();
\r
7880 is->error = NO_ERROR;
\r
7881 is->next += is->count;
\r
7882 if (is->count == 0 && is->second == is) {
\r
7883 /* End of file on stderr; quit with no message */
\r
7887 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7889 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7891 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7897 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7901 is = (InputSource *) lParam;
\r
7902 if (is->lineByLine) {
\r
7903 /* Feed in lines one by one */
\r
7904 char *p = is->buf;
\r
7906 while (q < is->next) {
\r
7907 if (*q++ == '\n') {
\r
7908 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7913 /* Move any partial line to the start of the buffer */
\r
7915 while (p < is->next) {
\r
7920 if (is->error != NO_ERROR || is->count == 0) {
\r
7921 /* Notify backend of the error. Note: If there was a partial
\r
7922 line at the end, it is not flushed through. */
\r
7923 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7926 /* Feed in the whole chunk of input at once */
\r
7927 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7928 is->next = is->buf;
\r
7932 /*---------------------------------------------------------------------------*\
\r
7934 * Menu enables. Used when setting various modes.
\r
7936 \*---------------------------------------------------------------------------*/
\r
7944 GreyRevert(Boolean grey)
\r
7945 { // [HGM] vari: for retracting variations in local mode
\r
7946 HMENU hmenu = GetMenu(hwndMain);
\r
7947 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7948 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7952 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7954 while (enab->item > 0) {
\r
7955 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7960 Enables gnuEnables[] = {
\r
7961 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7962 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7963 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7964 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7965 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7966 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7967 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7968 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7969 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7970 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7971 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7972 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7973 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7975 // Needed to switch from ncp to GNU mode on Engine Load
\r
7976 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7977 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7978 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7979 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7980 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7981 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7982 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
7983 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7984 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
7985 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
7986 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7987 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7988 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7989 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7993 Enables icsEnables[] = {
\r
7994 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7995 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7996 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7997 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7998 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7999 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8000 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
8001 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
8002 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
8003 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
8004 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8005 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
8006 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
8007 { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },
\r
8008 { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },
\r
8009 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
8010 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
8011 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
8012 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
8013 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
8018 Enables zippyEnables[] = {
\r
8019 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8020 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
8021 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
8022 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
8027 Enables ncpEnables[] = {
\r
8028 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
8029 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
8030 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8031 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8032 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8033 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8034 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
8035 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
8036 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
8037 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
8038 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
8039 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
8040 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8041 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
8042 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
8043 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
8044 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
8045 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
8046 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
8047 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
8048 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
8049 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
8053 Enables trainingOnEnables[] = {
\r
8054 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
8055 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
8056 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
8057 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
8058 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
8059 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
8060 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
8061 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8062 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
8066 Enables trainingOffEnables[] = {
\r
8067 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
8068 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
8069 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
8070 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
8071 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
8072 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
8073 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
8074 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8075 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
8079 /* These modify either ncpEnables or gnuEnables */
\r
8080 Enables cmailEnables[] = {
\r
8081 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
8082 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
8083 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
8084 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
8085 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
8086 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
8087 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
8091 Enables machineThinkingEnables[] = {
\r
8092 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8093 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
8094 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
8095 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8096 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
8097 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8098 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8099 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8100 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8101 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
8102 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8103 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8104 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8105 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8106 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
8107 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
8111 Enables userThinkingEnables[] = {
\r
8112 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8113 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
8114 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
8115 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8116 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
8117 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8118 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8119 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8120 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8121 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
8122 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
8123 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
8124 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
8125 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
8126 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
8127 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
8131 /*---------------------------------------------------------------------------*\
\r
8133 * Front-end interface functions exported by XBoard.
\r
8134 * Functions appear in same order as prototypes in frontend.h.
\r
8136 \*---------------------------------------------------------------------------*/
\r
8138 CheckMark(UINT item, int state)
\r
8140 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
8146 static UINT prevChecked = 0;
\r
8147 static int prevPausing = 0;
\r
8150 if (pausing != prevPausing) {
\r
8151 prevPausing = pausing;
\r
8152 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
8153 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
8154 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
8157 switch (gameMode) {
\r
8158 case BeginningOfGame:
\r
8159 if (appData.icsActive)
\r
8160 nowChecked = IDM_IcsClient;
\r
8161 else if (appData.noChessProgram)
\r
8162 nowChecked = IDM_EditGame;
\r
8164 nowChecked = IDM_MachineBlack;
\r
8166 case MachinePlaysBlack:
\r
8167 nowChecked = IDM_MachineBlack;
\r
8169 case MachinePlaysWhite:
\r
8170 nowChecked = IDM_MachineWhite;
\r
8172 case TwoMachinesPlay:
\r
8173 nowChecked = IDM_TwoMachines;
\r
8176 nowChecked = IDM_AnalysisMode;
\r
8179 nowChecked = IDM_AnalyzeFile;
\r
8182 nowChecked = IDM_EditGame;
\r
8184 case PlayFromGameFile:
\r
8185 nowChecked = IDM_LoadGame;
\r
8187 case EditPosition:
\r
8188 nowChecked = IDM_EditPosition;
\r
8191 nowChecked = IDM_Training;
\r
8193 case IcsPlayingWhite:
\r
8194 case IcsPlayingBlack:
\r
8195 case IcsObserving:
\r
8197 nowChecked = IDM_IcsClient;
\r
8204 CheckMark(prevChecked, MF_UNCHECKED);
\r
8205 CheckMark(nowChecked, MF_CHECKED);
\r
8206 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8208 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8209 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8210 MF_BYCOMMAND|MF_ENABLED);
\r
8212 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8213 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8216 prevChecked = nowChecked;
\r
8218 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8219 if (appData.icsActive) {
\r
8220 if (appData.icsEngineAnalyze) {
\r
8221 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8223 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8226 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8232 HMENU hmenu = GetMenu(hwndMain);
\r
8233 SetMenuEnables(hmenu, icsEnables);
\r
8234 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8235 MF_BYCOMMAND|MF_ENABLED);
\r
8237 if (appData.zippyPlay) {
\r
8238 SetMenuEnables(hmenu, zippyEnables);
\r
8239 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8240 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8241 MF_BYCOMMAND|MF_ENABLED);
\r
8249 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8255 HMENU hmenu = GetMenu(hwndMain);
\r
8256 SetMenuEnables(hmenu, ncpEnables);
\r
8257 DrawMenuBar(hwndMain);
\r
8263 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8267 SetTrainingModeOn()
\r
8270 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8271 for (i = 0; i < N_BUTTONS; i++) {
\r
8272 if (buttonDesc[i].hwnd != NULL)
\r
8273 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8278 VOID SetTrainingModeOff()
\r
8281 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8282 for (i = 0; i < N_BUTTONS; i++) {
\r
8283 if (buttonDesc[i].hwnd != NULL)
\r
8284 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8290 SetUserThinkingEnables()
\r
8292 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8296 SetMachineThinkingEnables()
\r
8298 HMENU hMenu = GetMenu(hwndMain);
\r
8299 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8301 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8303 if (gameMode == MachinePlaysBlack) {
\r
8304 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8305 } else if (gameMode == MachinePlaysWhite) {
\r
8306 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8307 } else if (gameMode == TwoMachinesPlay) {
\r
8308 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8314 DisplayTitle(char *str)
\r
8316 char title[MSG_SIZ], *host;
\r
8317 if (str[0] != NULLCHAR) {
\r
8318 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8319 } else if (appData.icsActive) {
\r
8320 if (appData.icsCommPort[0] != NULLCHAR)
\r
8323 host = appData.icsHost;
\r
8324 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8325 } else if (appData.noChessProgram) {
\r
8326 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8328 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8329 strcat(title, ": ");
\r
8330 strcat(title, first.tidy);
\r
8332 SetWindowText(hwndMain, title);
\r
8337 DisplayMessage(char *str1, char *str2)
\r
8341 int remain = MESSAGE_TEXT_MAX - 1;
\r
8344 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8345 messageText[0] = NULLCHAR;
\r
8347 len = strlen(str1);
\r
8348 if (len > remain) len = remain;
\r
8349 strncpy(messageText, str1, len);
\r
8350 messageText[len] = NULLCHAR;
\r
8353 if (*str2 && remain >= 2) {
\r
8355 strcat(messageText, " ");
\r
8358 len = strlen(str2);
\r
8359 if (len > remain) len = remain;
\r
8360 strncat(messageText, str2, len);
\r
8362 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8363 safeStrCpy(lastMsg, messageText, MSG_SIZ);
\r
8365 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8369 hdc = GetDC(hwndMain);
\r
8370 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8371 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8372 &messageRect, messageText, strlen(messageText), NULL);
\r
8373 (void) SelectObject(hdc, oldFont);
\r
8374 (void) ReleaseDC(hwndMain, hdc);
\r
8378 DisplayError(char *str, int error)
\r
8380 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8384 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8386 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8387 NULL, error, LANG_NEUTRAL,
\r
8388 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8390 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8392 ErrorMap *em = errmap;
\r
8393 while (em->err != 0 && em->err != error) em++;
\r
8394 if (em->err != 0) {
\r
8395 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8397 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8402 ErrorPopUp(_("Error"), buf);
\r
8407 DisplayMoveError(char *str)
\r
8409 fromX = fromY = -1;
\r
8410 ClearHighlights();
\r
8411 DrawPosition(FALSE, NULL);
\r
8412 if (appData.popupMoveErrors) {
\r
8413 ErrorPopUp(_("Error"), str);
\r
8415 DisplayMessage(str, "");
\r
8416 moveErrorMessageUp = TRUE;
\r
8421 DisplayFatalError(char *str, int error, int exitStatus)
\r
8423 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8425 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8428 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8429 NULL, error, LANG_NEUTRAL,
\r
8430 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8432 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8434 ErrorMap *em = errmap;
\r
8435 while (em->err != 0 && em->err != error) em++;
\r
8436 if (em->err != 0) {
\r
8437 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8439 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8444 if (appData.debugMode) {
\r
8445 fprintf(debugFP, "%s: %s\n", label, str);
\r
8447 if (appData.popupExitMessage) {
\r
8448 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8449 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8451 ExitEvent(exitStatus);
\r
8456 DisplayInformation(char *str)
\r
8458 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8463 DisplayNote(char *str)
\r
8465 ErrorPopUp(_("Note"), str);
\r
8470 char *title, *question, *replyPrefix;
\r
8475 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8477 static QuestionParams *qp;
\r
8478 char reply[MSG_SIZ];
\r
8481 switch (message) {
\r
8482 case WM_INITDIALOG:
\r
8483 qp = (QuestionParams *) lParam;
\r
8484 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8485 Translate(hDlg, DLG_Question);
\r
8486 SetWindowText(hDlg, qp->title);
\r
8487 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8488 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8492 switch (LOWORD(wParam)) {
\r
8494 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8495 if (*reply) strcat(reply, " ");
\r
8496 len = strlen(reply);
\r
8497 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8498 strcat(reply, "\n");
\r
8499 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8500 EndDialog(hDlg, TRUE);
\r
8501 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8504 EndDialog(hDlg, FALSE);
\r
8515 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8517 QuestionParams qp;
\r
8521 qp.question = question;
\r
8522 qp.replyPrefix = replyPrefix;
\r
8524 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8525 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8526 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8527 FreeProcInstance(lpProc);
\r
8530 /* [AS] Pick FRC position */
\r
8531 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8533 static int * lpIndexFRC;
\r
8539 case WM_INITDIALOG:
\r
8540 lpIndexFRC = (int *) lParam;
\r
8542 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8543 Translate(hDlg, DLG_NewGameFRC);
\r
8545 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8546 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8547 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8548 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8553 switch( LOWORD(wParam) ) {
\r
8555 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8556 EndDialog( hDlg, 0 );
\r
8557 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8560 EndDialog( hDlg, 1 );
\r
8562 case IDC_NFG_Edit:
\r
8563 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8564 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8566 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8569 case IDC_NFG_Random:
\r
8570 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8571 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8584 int index = appData.defaultFrcPosition;
\r
8585 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8587 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8589 if( result == 0 ) {
\r
8590 appData.defaultFrcPosition = index;
\r
8596 /* [AS] Game list options. Refactored by HGM */
\r
8598 HWND gameListOptionsDialog;
\r
8600 // low-level front-end: clear text edit / list widget
\r
8605 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8608 // low-level front-end: clear text edit / list widget
\r
8610 GLT_DeSelectList()
\r
8612 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8615 // low-level front-end: append line to text edit / list widget
\r
8617 GLT_AddToList( char *name )
\r
8620 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8624 // low-level front-end: get line from text edit / list widget
\r
8626 GLT_GetFromList( int index, char *name )
\r
8629 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8635 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8637 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8638 int idx2 = idx1 + delta;
\r
8639 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8641 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8644 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8645 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8646 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8647 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8651 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8655 case WM_INITDIALOG:
\r
8656 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8658 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8659 Translate(hDlg, DLG_GameListOptions);
\r
8661 /* Initialize list */
\r
8662 GLT_TagsToList( lpUserGLT );
\r
8664 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8669 switch( LOWORD(wParam) ) {
\r
8672 EndDialog( hDlg, 0 );
\r
8675 EndDialog( hDlg, 1 );
\r
8678 case IDC_GLT_Default:
\r
8679 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8682 case IDC_GLT_Restore:
\r
8683 GLT_TagsToList( appData.gameListTags );
\r
8687 GLT_MoveSelection( hDlg, -1 );
\r
8690 case IDC_GLT_Down:
\r
8691 GLT_MoveSelection( hDlg, +1 );
\r
8701 int GameListOptions()
\r
8704 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8706 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8708 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8710 if( result == 0 ) {
\r
8711 /* [AS] Memory leak here! */
\r
8712 appData.gameListTags = strdup( lpUserGLT );
\r
8719 DisplayIcsInteractionTitle(char *str)
\r
8721 char consoleTitle[MSG_SIZ];
\r
8723 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8724 SetWindowText(hwndConsole, consoleTitle);
\r
8726 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
8727 char buf[MSG_SIZ], *p = buf, *q;
\r
8728 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
8730 q = strchr(p, ';');
\r
8732 if(*p) ChatPopUp(p);
\r
8736 SetActiveWindow(hwndMain);
\r
8740 DrawPosition(int fullRedraw, Board board)
\r
8742 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8745 void NotifyFrontendLogin()
\r
8748 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8754 fromX = fromY = -1;
\r
8755 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8756 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8757 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8758 dragInfo.lastpos = dragInfo.pos;
\r
8759 dragInfo.start.x = dragInfo.start.y = -1;
\r
8760 dragInfo.from = dragInfo.start;
\r
8762 DrawPosition(TRUE, NULL);
\r
8769 CommentPopUp(char *title, char *str)
\r
8771 HWND hwnd = GetActiveWindow();
\r
8772 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8774 SetActiveWindow(hwnd);
\r
8778 CommentPopDown(void)
\r
8780 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8781 if (commentDialog) {
\r
8782 ShowWindow(commentDialog, SW_HIDE);
\r
8784 commentUp = FALSE;
\r
8788 EditCommentPopUp(int index, char *title, char *str)
\r
8790 EitherCommentPopUp(index, title, str, TRUE);
\r
8797 MyPlaySound(&sounds[(int)SoundRoar]);
\r
8804 MyPlaySound(&sounds[(int)SoundMove]);
\r
8807 VOID PlayIcsWinSound()
\r
8809 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8812 VOID PlayIcsLossSound()
\r
8814 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8817 VOID PlayIcsDrawSound()
\r
8819 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8822 VOID PlayIcsUnfinishedSound()
\r
8824 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8830 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8836 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8844 consoleEcho = TRUE;
\r
8845 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8846 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8847 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8856 consoleEcho = FALSE;
\r
8857 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8858 /* This works OK: set text and background both to the same color */
\r
8860 cf.crTextColor = COLOR_ECHOOFF;
\r
8861 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8862 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8865 /* No Raw()...? */
\r
8867 void Colorize(ColorClass cc, int continuation)
\r
8869 currentColorClass = cc;
\r
8870 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8871 consoleCF.crTextColor = textAttribs[cc].color;
\r
8872 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8873 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8879 static char buf[MSG_SIZ];
\r
8880 DWORD bufsiz = MSG_SIZ;
\r
8882 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8883 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8885 if (!GetUserName(buf, &bufsiz)) {
\r
8886 /*DisplayError("Error getting user name", GetLastError());*/
\r
8887 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8895 static char buf[MSG_SIZ];
\r
8896 DWORD bufsiz = MSG_SIZ;
\r
8898 if (!GetComputerName(buf, &bufsiz)) {
\r
8899 /*DisplayError("Error getting host name", GetLastError());*/
\r
8900 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8907 ClockTimerRunning()
\r
8909 return clockTimerEvent != 0;
\r
8915 if (clockTimerEvent == 0) return FALSE;
\r
8916 KillTimer(hwndMain, clockTimerEvent);
\r
8917 clockTimerEvent = 0;
\r
8922 StartClockTimer(long millisec)
\r
8924 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8925 (UINT) millisec, NULL);
\r
8929 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8932 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8934 if(appData.noGUI) return;
\r
8935 hdc = GetDC(hwndMain);
\r
8936 if (!IsIconic(hwndMain)) {
\r
8937 DisplayAClock(hdc, timeRemaining, highlight,
\r
8938 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8940 if (highlight && iconCurrent == iconBlack) {
\r
8941 iconCurrent = iconWhite;
\r
8942 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8943 if (IsIconic(hwndMain)) {
\r
8944 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8947 (void) ReleaseDC(hwndMain, hdc);
\r
8949 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8953 DisplayBlackClock(long timeRemaining, int highlight)
\r
8956 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8959 if(appData.noGUI) return;
\r
8960 hdc = GetDC(hwndMain);
\r
8961 if (!IsIconic(hwndMain)) {
\r
8962 DisplayAClock(hdc, timeRemaining, highlight,
\r
8963 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8965 if (highlight && iconCurrent == iconWhite) {
\r
8966 iconCurrent = iconBlack;
\r
8967 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8968 if (IsIconic(hwndMain)) {
\r
8969 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8972 (void) ReleaseDC(hwndMain, hdc);
\r
8974 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8979 LoadGameTimerRunning()
\r
8981 return loadGameTimerEvent != 0;
\r
8985 StopLoadGameTimer()
\r
8987 if (loadGameTimerEvent == 0) return FALSE;
\r
8988 KillTimer(hwndMain, loadGameTimerEvent);
\r
8989 loadGameTimerEvent = 0;
\r
8994 StartLoadGameTimer(long millisec)
\r
8996 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8997 (UINT) millisec, NULL);
\r
9005 char fileTitle[MSG_SIZ];
\r
9007 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
9008 f = OpenFileDialog(hwndMain, "a", defName,
\r
9009 appData.oldSaveStyle ? "gam" : "pgn",
\r
9011 _("Save Game to File"), NULL, fileTitle, NULL);
\r
9013 SaveGame(f, 0, "");
\r
9020 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
9022 if (delayedTimerEvent != 0) {
\r
9023 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
9024 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
9026 KillTimer(hwndMain, delayedTimerEvent);
\r
9027 delayedTimerEvent = 0;
\r
9028 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
9029 delayedTimerCallback();
\r
9031 delayedTimerCallback = cb;
\r
9032 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
9033 (UINT) millisec, NULL);
\r
9036 DelayedEventCallback
\r
9039 if (delayedTimerEvent) {
\r
9040 return delayedTimerCallback;
\r
9047 CancelDelayedEvent()
\r
9049 if (delayedTimerEvent) {
\r
9050 KillTimer(hwndMain, delayedTimerEvent);
\r
9051 delayedTimerEvent = 0;
\r
9055 DWORD GetWin32Priority(int nice)
\r
9056 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
9058 REALTIME_PRIORITY_CLASS 0x00000100
\r
9059 HIGH_PRIORITY_CLASS 0x00000080
\r
9060 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
9061 NORMAL_PRIORITY_CLASS 0x00000020
\r
9062 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
9063 IDLE_PRIORITY_CLASS 0x00000040
\r
9065 if (nice < -15) return 0x00000080;
\r
9066 if (nice < 0) return 0x00008000;
\r
9067 if (nice == 0) return 0x00000020;
\r
9068 if (nice < 15) return 0x00004000;
\r
9069 return 0x00000040;
\r
9072 void RunCommand(char *cmdLine)
\r
9074 /* Now create the child process. */
\r
9075 STARTUPINFO siStartInfo;
\r
9076 PROCESS_INFORMATION piProcInfo;
\r
9078 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9079 siStartInfo.lpReserved = NULL;
\r
9080 siStartInfo.lpDesktop = NULL;
\r
9081 siStartInfo.lpTitle = NULL;
\r
9082 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9083 siStartInfo.cbReserved2 = 0;
\r
9084 siStartInfo.lpReserved2 = NULL;
\r
9085 siStartInfo.hStdInput = NULL;
\r
9086 siStartInfo.hStdOutput = NULL;
\r
9087 siStartInfo.hStdError = NULL;
\r
9089 CreateProcess(NULL,
\r
9090 cmdLine, /* command line */
\r
9091 NULL, /* process security attributes */
\r
9092 NULL, /* primary thread security attrs */
\r
9093 TRUE, /* handles are inherited */
\r
9094 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9095 NULL, /* use parent's environment */
\r
9097 &siStartInfo, /* STARTUPINFO pointer */
\r
9098 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9100 CloseHandle(piProcInfo.hThread);
\r
9103 /* Start a child process running the given program.
\r
9104 The process's standard output can be read from "from", and its
\r
9105 standard input can be written to "to".
\r
9106 Exit with fatal error if anything goes wrong.
\r
9107 Returns an opaque pointer that can be used to destroy the process
\r
9111 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
9113 #define BUFSIZE 4096
\r
9115 HANDLE hChildStdinRd, hChildStdinWr,
\r
9116 hChildStdoutRd, hChildStdoutWr;
\r
9117 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
9118 SECURITY_ATTRIBUTES saAttr;
\r
9120 PROCESS_INFORMATION piProcInfo;
\r
9121 STARTUPINFO siStartInfo;
\r
9123 char buf[MSG_SIZ];
\r
9126 if (appData.debugMode) {
\r
9127 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
9132 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
9133 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
9134 saAttr.bInheritHandle = TRUE;
\r
9135 saAttr.lpSecurityDescriptor = NULL;
\r
9138 * The steps for redirecting child's STDOUT:
\r
9139 * 1. Create anonymous pipe to be STDOUT for child.
\r
9140 * 2. Create a noninheritable duplicate of read handle,
\r
9141 * and close the inheritable read handle.
\r
9144 /* Create a pipe for the child's STDOUT. */
\r
9145 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
9146 return GetLastError();
\r
9149 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
9150 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
9151 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
9152 FALSE, /* not inherited */
\r
9153 DUPLICATE_SAME_ACCESS);
\r
9155 return GetLastError();
\r
9157 CloseHandle(hChildStdoutRd);
\r
9160 * The steps for redirecting child's STDIN:
\r
9161 * 1. Create anonymous pipe to be STDIN for child.
\r
9162 * 2. Create a noninheritable duplicate of write handle,
\r
9163 * and close the inheritable write handle.
\r
9166 /* Create a pipe for the child's STDIN. */
\r
9167 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
9168 return GetLastError();
\r
9171 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
9172 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
9173 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
9174 FALSE, /* not inherited */
\r
9175 DUPLICATE_SAME_ACCESS);
\r
9177 return GetLastError();
\r
9179 CloseHandle(hChildStdinWr);
\r
9181 /* Arrange to (1) look in dir for the child .exe file, and
\r
9182 * (2) have dir be the child's working directory. Interpret
\r
9183 * dir relative to the directory WinBoard loaded from. */
\r
9184 GetCurrentDirectory(MSG_SIZ, buf);
\r
9185 SetCurrentDirectory(installDir);
\r
9186 SetCurrentDirectory(dir);
\r
9188 /* Now create the child process. */
\r
9190 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9191 siStartInfo.lpReserved = NULL;
\r
9192 siStartInfo.lpDesktop = NULL;
\r
9193 siStartInfo.lpTitle = NULL;
\r
9194 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9195 siStartInfo.cbReserved2 = 0;
\r
9196 siStartInfo.lpReserved2 = NULL;
\r
9197 siStartInfo.hStdInput = hChildStdinRd;
\r
9198 siStartInfo.hStdOutput = hChildStdoutWr;
\r
9199 siStartInfo.hStdError = hChildStdoutWr;
\r
9201 fSuccess = CreateProcess(NULL,
\r
9202 cmdLine, /* command line */
\r
9203 NULL, /* process security attributes */
\r
9204 NULL, /* primary thread security attrs */
\r
9205 TRUE, /* handles are inherited */
\r
9206 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9207 NULL, /* use parent's environment */
\r
9209 &siStartInfo, /* STARTUPINFO pointer */
\r
9210 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9212 err = GetLastError();
\r
9213 SetCurrentDirectory(buf); /* return to prev directory */
\r
9218 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
9219 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
9220 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
9223 /* Close the handles we don't need in the parent */
\r
9224 CloseHandle(piProcInfo.hThread);
\r
9225 CloseHandle(hChildStdinRd);
\r
9226 CloseHandle(hChildStdoutWr);
\r
9228 /* Prepare return value */
\r
9229 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9230 cp->kind = CPReal;
\r
9231 cp->hProcess = piProcInfo.hProcess;
\r
9232 cp->pid = piProcInfo.dwProcessId;
\r
9233 cp->hFrom = hChildStdoutRdDup;
\r
9234 cp->hTo = hChildStdinWrDup;
\r
9236 *pr = (void *) cp;
\r
9238 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
9239 2000 where engines sometimes don't see the initial command(s)
\r
9240 from WinBoard and hang. I don't understand how that can happen,
\r
9241 but the Sleep is harmless, so I've put it in. Others have also
\r
9242 reported what may be the same problem, so hopefully this will fix
\r
9243 it for them too. */
\r
9251 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
9253 ChildProc *cp; int result;
\r
9255 cp = (ChildProc *) pr;
\r
9256 if (cp == NULL) return;
\r
9258 switch (cp->kind) {
\r
9260 /* TerminateProcess is considered harmful, so... */
\r
9261 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9262 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9263 /* The following doesn't work because the chess program
\r
9264 doesn't "have the same console" as WinBoard. Maybe
\r
9265 we could arrange for this even though neither WinBoard
\r
9266 nor the chess program uses a console for stdio? */
\r
9267 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9269 /* [AS] Special termination modes for misbehaving programs... */
\r
9270 if( signal & 8 ) {
\r
9271 result = TerminateProcess( cp->hProcess, 0 );
\r
9273 if ( appData.debugMode) {
\r
9274 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9277 else if( signal & 4 ) {
\r
9278 DWORD dw = WaitForSingleObject( cp->hProcess, appData.delayAfterQuit*1000 + 50 ); // Wait 3 seconds at most
\r
9280 if( dw != WAIT_OBJECT_0 ) {
\r
9281 result = TerminateProcess( cp->hProcess, 0 );
\r
9283 if ( appData.debugMode) {
\r
9284 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9290 CloseHandle(cp->hProcess);
\r
9294 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9298 closesocket(cp->sock);
\r
9303 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9304 closesocket(cp->sock);
\r
9305 closesocket(cp->sock2);
\r
9313 InterruptChildProcess(ProcRef pr)
\r
9317 cp = (ChildProc *) pr;
\r
9318 if (cp == NULL) return;
\r
9319 switch (cp->kind) {
\r
9321 /* The following doesn't work because the chess program
\r
9322 doesn't "have the same console" as WinBoard. Maybe
\r
9323 we could arrange for this even though neither WinBoard
\r
9324 nor the chess program uses a console for stdio */
\r
9325 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9330 /* Can't interrupt */
\r
9334 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9341 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9343 char cmdLine[MSG_SIZ];
\r
9345 if (port[0] == NULLCHAR) {
\r
9346 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9348 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9350 return StartChildProcess(cmdLine, "", pr);
\r
9354 /* Code to open TCP sockets */
\r
9357 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9363 struct sockaddr_in sa, mysa;
\r
9364 struct hostent FAR *hp;
\r
9365 unsigned short uport;
\r
9366 WORD wVersionRequested;
\r
9369 /* Initialize socket DLL */
\r
9370 wVersionRequested = MAKEWORD(1, 1);
\r
9371 err = WSAStartup(wVersionRequested, &wsaData);
\r
9372 if (err != 0) return err;
\r
9375 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9376 err = WSAGetLastError();
\r
9381 /* Bind local address using (mostly) don't-care values.
\r
9383 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9384 mysa.sin_family = AF_INET;
\r
9385 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9386 uport = (unsigned short) 0;
\r
9387 mysa.sin_port = htons(uport);
\r
9388 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9389 == SOCKET_ERROR) {
\r
9390 err = WSAGetLastError();
\r
9395 /* Resolve remote host name */
\r
9396 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9397 if (!(hp = gethostbyname(host))) {
\r
9398 unsigned int b0, b1, b2, b3;
\r
9400 err = WSAGetLastError();
\r
9402 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9403 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9404 hp->h_addrtype = AF_INET;
\r
9406 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9407 hp->h_addr_list[0] = (char *) malloc(4);
\r
9408 hp->h_addr_list[0][0] = (char) b0;
\r
9409 hp->h_addr_list[0][1] = (char) b1;
\r
9410 hp->h_addr_list[0][2] = (char) b2;
\r
9411 hp->h_addr_list[0][3] = (char) b3;
\r
9417 sa.sin_family = hp->h_addrtype;
\r
9418 uport = (unsigned short) atoi(port);
\r
9419 sa.sin_port = htons(uport);
\r
9420 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9422 /* Make connection */
\r
9423 if (connect(s, (struct sockaddr *) &sa,
\r
9424 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9425 err = WSAGetLastError();
\r
9430 /* Prepare return value */
\r
9431 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9432 cp->kind = CPSock;
\r
9434 *pr = (ProcRef *) cp;
\r
9440 OpenCommPort(char *name, ProcRef *pr)
\r
9445 char fullname[MSG_SIZ];
\r
9447 if (*name != '\\')
\r
9448 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9450 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9452 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9453 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9454 if (h == (HANDLE) -1) {
\r
9455 return GetLastError();
\r
9459 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9461 /* Accumulate characters until a 100ms pause, then parse */
\r
9462 ct.ReadIntervalTimeout = 100;
\r
9463 ct.ReadTotalTimeoutMultiplier = 0;
\r
9464 ct.ReadTotalTimeoutConstant = 0;
\r
9465 ct.WriteTotalTimeoutMultiplier = 0;
\r
9466 ct.WriteTotalTimeoutConstant = 0;
\r
9467 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9469 /* Prepare return value */
\r
9470 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9471 cp->kind = CPComm;
\r
9474 *pr = (ProcRef *) cp;
\r
9480 OpenLoopback(ProcRef *pr)
\r
9482 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9488 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9493 struct sockaddr_in sa, mysa;
\r
9494 struct hostent FAR *hp;
\r
9495 unsigned short uport;
\r
9496 WORD wVersionRequested;
\r
9499 char stderrPortStr[MSG_SIZ];
\r
9501 /* Initialize socket DLL */
\r
9502 wVersionRequested = MAKEWORD(1, 1);
\r
9503 err = WSAStartup(wVersionRequested, &wsaData);
\r
9504 if (err != 0) return err;
\r
9506 /* Resolve remote host name */
\r
9507 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9508 if (!(hp = gethostbyname(host))) {
\r
9509 unsigned int b0, b1, b2, b3;
\r
9511 err = WSAGetLastError();
\r
9513 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9514 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9515 hp->h_addrtype = AF_INET;
\r
9517 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9518 hp->h_addr_list[0] = (char *) malloc(4);
\r
9519 hp->h_addr_list[0][0] = (char) b0;
\r
9520 hp->h_addr_list[0][1] = (char) b1;
\r
9521 hp->h_addr_list[0][2] = (char) b2;
\r
9522 hp->h_addr_list[0][3] = (char) b3;
\r
9528 sa.sin_family = hp->h_addrtype;
\r
9529 uport = (unsigned short) 514;
\r
9530 sa.sin_port = htons(uport);
\r
9531 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9533 /* Bind local socket to unused "privileged" port address
\r
9535 s = INVALID_SOCKET;
\r
9536 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9537 mysa.sin_family = AF_INET;
\r
9538 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9539 for (fromPort = 1023;; fromPort--) {
\r
9540 if (fromPort < 0) {
\r
9542 return WSAEADDRINUSE;
\r
9544 if (s == INVALID_SOCKET) {
\r
9545 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9546 err = WSAGetLastError();
\r
9551 uport = (unsigned short) fromPort;
\r
9552 mysa.sin_port = htons(uport);
\r
9553 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9554 == SOCKET_ERROR) {
\r
9555 err = WSAGetLastError();
\r
9556 if (err == WSAEADDRINUSE) continue;
\r
9560 if (connect(s, (struct sockaddr *) &sa,
\r
9561 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9562 err = WSAGetLastError();
\r
9563 if (err == WSAEADDRINUSE) {
\r
9574 /* Bind stderr local socket to unused "privileged" port address
\r
9576 s2 = INVALID_SOCKET;
\r
9577 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9578 mysa.sin_family = AF_INET;
\r
9579 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9580 for (fromPort = 1023;; fromPort--) {
\r
9581 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9582 if (fromPort < 0) {
\r
9583 (void) closesocket(s);
\r
9585 return WSAEADDRINUSE;
\r
9587 if (s2 == INVALID_SOCKET) {
\r
9588 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9589 err = WSAGetLastError();
\r
9595 uport = (unsigned short) fromPort;
\r
9596 mysa.sin_port = htons(uport);
\r
9597 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9598 == SOCKET_ERROR) {
\r
9599 err = WSAGetLastError();
\r
9600 if (err == WSAEADDRINUSE) continue;
\r
9601 (void) closesocket(s);
\r
9605 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9606 err = WSAGetLastError();
\r
9607 if (err == WSAEADDRINUSE) {
\r
9609 s2 = INVALID_SOCKET;
\r
9612 (void) closesocket(s);
\r
9613 (void) closesocket(s2);
\r
9619 prevStderrPort = fromPort; // remember port used
\r
9620 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9622 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9623 err = WSAGetLastError();
\r
9624 (void) closesocket(s);
\r
9625 (void) closesocket(s2);
\r
9630 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9631 err = WSAGetLastError();
\r
9632 (void) closesocket(s);
\r
9633 (void) closesocket(s2);
\r
9637 if (*user == NULLCHAR) user = UserName();
\r
9638 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9639 err = WSAGetLastError();
\r
9640 (void) closesocket(s);
\r
9641 (void) closesocket(s2);
\r
9645 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9646 err = WSAGetLastError();
\r
9647 (void) closesocket(s);
\r
9648 (void) closesocket(s2);
\r
9653 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9654 err = WSAGetLastError();
\r
9655 (void) closesocket(s);
\r
9656 (void) closesocket(s2);
\r
9660 (void) closesocket(s2); /* Stop listening */
\r
9662 /* Prepare return value */
\r
9663 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9664 cp->kind = CPRcmd;
\r
9667 *pr = (ProcRef *) cp;
\r
9674 AddInputSource(ProcRef pr, int lineByLine,
\r
9675 InputCallback func, VOIDSTAR closure)
\r
9677 InputSource *is, *is2 = NULL;
\r
9678 ChildProc *cp = (ChildProc *) pr;
\r
9680 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9681 is->lineByLine = lineByLine;
\r
9683 is->closure = closure;
\r
9684 is->second = NULL;
\r
9685 is->next = is->buf;
\r
9686 if (pr == NoProc) {
\r
9687 is->kind = CPReal;
\r
9688 consoleInputSource = is;
\r
9690 is->kind = cp->kind;
\r
9692 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9693 we create all threads suspended so that the is->hThread variable can be
\r
9694 safely assigned, then let the threads start with ResumeThread.
\r
9696 switch (cp->kind) {
\r
9698 is->hFile = cp->hFrom;
\r
9699 cp->hFrom = NULL; /* now owned by InputThread */
\r
9701 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9702 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9706 is->hFile = cp->hFrom;
\r
9707 cp->hFrom = NULL; /* now owned by InputThread */
\r
9709 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9710 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9714 is->sock = cp->sock;
\r
9716 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9717 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9721 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9723 is->sock = cp->sock;
\r
9725 is2->sock = cp->sock2;
\r
9726 is2->second = is2;
\r
9728 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9729 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9731 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9732 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9736 if( is->hThread != NULL ) {
\r
9737 ResumeThread( is->hThread );
\r
9740 if( is2 != NULL && is2->hThread != NULL ) {
\r
9741 ResumeThread( is2->hThread );
\r
9745 return (InputSourceRef) is;
\r
9749 RemoveInputSource(InputSourceRef isr)
\r
9753 is = (InputSource *) isr;
\r
9754 is->hThread = NULL; /* tell thread to stop */
\r
9755 CloseHandle(is->hThread);
\r
9756 if (is->second != NULL) {
\r
9757 is->second->hThread = NULL;
\r
9758 CloseHandle(is->second->hThread);
\r
9762 int no_wrap(char *message, int count)
\r
9764 ConsoleOutput(message, count, FALSE);
\r
9769 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9772 int outCount = SOCKET_ERROR;
\r
9773 ChildProc *cp = (ChildProc *) pr;
\r
9774 static OVERLAPPED ovl;
\r
9775 static int line = 0;
\r
9779 if (appData.noJoin || !appData.useInternalWrap)
\r
9780 return no_wrap(message, count);
\r
9783 int width = get_term_width();
\r
9784 int len = wrap(NULL, message, count, width, &line);
\r
9785 char *msg = malloc(len);
\r
9789 return no_wrap(message, count);
\r
9792 dbgchk = wrap(msg, message, count, width, &line);
\r
9793 if (dbgchk != len && appData.debugMode)
\r
9794 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9795 ConsoleOutput(msg, len, FALSE);
\r
9802 if (ovl.hEvent == NULL) {
\r
9803 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9805 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9807 switch (cp->kind) {
\r
9810 outCount = send(cp->sock, message, count, 0);
\r
9811 if (outCount == SOCKET_ERROR) {
\r
9812 *outError = WSAGetLastError();
\r
9814 *outError = NO_ERROR;
\r
9819 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9820 &dOutCount, NULL)) {
\r
9821 *outError = NO_ERROR;
\r
9822 outCount = (int) dOutCount;
\r
9824 *outError = GetLastError();
\r
9829 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9830 &dOutCount, &ovl);
\r
9831 if (*outError == NO_ERROR) {
\r
9832 outCount = (int) dOutCount;
\r
9842 if(n != 0) Sleep(n);
\r
9846 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9849 /* Ignore delay, not implemented for WinBoard */
\r
9850 return OutputToProcess(pr, message, count, outError);
\r
9855 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9856 char *buf, int count, int error)
\r
9858 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9861 /* see wgamelist.c for Game List functions */
\r
9862 /* see wedittags.c for Edit Tags functions */
\r
9869 char buf[MSG_SIZ];
\r
9872 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9873 f = fopen(buf, "r");
\r
9875 ProcessICSInitScript(f);
\r
9885 StartAnalysisClock()
\r
9887 if (analysisTimerEvent) return;
\r
9888 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9889 (UINT) 2000, NULL);
\r
9893 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9895 highlightInfo.sq[0].x = fromX;
\r
9896 highlightInfo.sq[0].y = fromY;
\r
9897 highlightInfo.sq[1].x = toX;
\r
9898 highlightInfo.sq[1].y = toY;
\r
9904 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9905 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9909 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9911 premoveHighlightInfo.sq[0].x = fromX;
\r
9912 premoveHighlightInfo.sq[0].y = fromY;
\r
9913 premoveHighlightInfo.sq[1].x = toX;
\r
9914 premoveHighlightInfo.sq[1].y = toY;
\r
9918 ClearPremoveHighlights()
\r
9920 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9921 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9925 ShutDownFrontEnd()
\r
9927 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9928 DeleteClipboardTempFiles();
\r
9934 if (IsIconic(hwndMain))
\r
9935 ShowWindow(hwndMain, SW_RESTORE);
\r
9937 SetActiveWindow(hwndMain);
\r
9941 * Prototypes for animation support routines
\r
9943 static void ScreenSquare(int column, int row, POINT * pt);
\r
9944 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9945 POINT frames[], int * nFrames);
\r
9951 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9952 { // [HGM] atomic: animate blast wave
\r
9955 explodeInfo.fromX = fromX;
\r
9956 explodeInfo.fromY = fromY;
\r
9957 explodeInfo.toX = toX;
\r
9958 explodeInfo.toY = toY;
\r
9959 for(i=1; i<4*kFactor; i++) {
\r
9960 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9961 DrawPosition(FALSE, board);
\r
9962 Sleep(appData.animSpeed);
\r
9964 explodeInfo.radius = 0;
\r
9965 DrawPosition(TRUE, board);
\r
9969 AnimateMove(board, fromX, fromY, toX, toY)
\r
9976 ChessSquare piece;
\r
9977 int x = toX, y = toY;
\r
9978 POINT start, finish, mid;
\r
9979 POINT frames[kFactor * 2 + 1];
\r
9982 if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();
\r
9984 if (!appData.animate) return;
\r
9985 if (doingSizing) return;
\r
9986 if (fromY < 0 || fromX < 0) return;
\r
9987 piece = board[fromY][fromX];
\r
9988 if (piece >= EmptySquare) return;
\r
9990 if(killX >= 0) toX = killX, toY = killY; // [HGM] lion: first to kill square
\r
9994 ScreenSquare(fromX, fromY, &start);
\r
9995 ScreenSquare(toX, toY, &finish);
\r
9997 /* All moves except knight jumps move in straight line */
\r
9998 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9999 mid.x = start.x + (finish.x - start.x) / 2;
\r
10000 mid.y = start.y + (finish.y - start.y) / 2;
\r
10002 /* Knight: make straight movement then diagonal */
\r
10003 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
10004 mid.x = start.x + (finish.x - start.x) / 2;
\r
10008 mid.y = start.y + (finish.y - start.y) / 2;
\r
10012 /* Don't use as many frames for very short moves */
\r
10013 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
10014 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
10016 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
10018 animInfo.from.x = fromX;
\r
10019 animInfo.from.y = fromY;
\r
10020 animInfo.to.x = toX;
\r
10021 animInfo.to.y = toY;
\r
10022 animInfo.lastpos = start;
\r
10023 animInfo.piece = piece;
\r
10024 for (n = 0; n < nFrames; n++) {
\r
10025 animInfo.pos = frames[n];
\r
10026 DrawPosition(FALSE, NULL);
\r
10027 animInfo.lastpos = animInfo.pos;
\r
10028 Sleep(appData.animSpeed);
\r
10030 animInfo.pos = finish;
\r
10031 DrawPosition(FALSE, NULL);
\r
10033 if(toX != x || toY != y) { fromX = toX; fromY = toY; toX = x; toY = y; goto again; } // second leg
\r
10035 animInfo.piece = EmptySquare;
\r
10036 Explode(board, fromX, fromY, toX, toY);
\r
10039 /* Convert board position to corner of screen rect and color */
\r
10042 ScreenSquare(column, row, pt)
\r
10043 int column; int row; POINT * pt;
\r
10046 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
10047 pt->y = lineGap + row * (squareSize + lineGap) + border;
\r
10049 pt->x = lineGap + column * (squareSize + lineGap) + border;
\r
10050 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
10054 /* Generate a series of frame coords from start->mid->finish.
\r
10055 The movement rate doubles until the half way point is
\r
10056 reached, then halves back down to the final destination,
\r
10057 which gives a nice slow in/out effect. The algorithmn
\r
10058 may seem to generate too many intermediates for short
\r
10059 moves, but remember that the purpose is to attract the
\r
10060 viewers attention to the piece about to be moved and
\r
10061 then to where it ends up. Too few frames would be less
\r
10065 Tween(start, mid, finish, factor, frames, nFrames)
\r
10066 POINT * start; POINT * mid;
\r
10067 POINT * finish; int factor;
\r
10068 POINT frames[]; int * nFrames;
\r
10070 int n, fraction = 1, count = 0;
\r
10072 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
10073 for (n = 0; n < factor; n++)
\r
10075 for (n = 0; n < factor; n++) {
\r
10076 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
10077 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
10079 fraction = fraction / 2;
\r
10083 frames[count] = *mid;
\r
10086 /* Slow out, stepping 1/2, then 1/4, ... */
\r
10088 for (n = 0; n < factor; n++) {
\r
10089 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
10090 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
10092 fraction = fraction * 2;
\r
10094 *nFrames = count;
\r
10098 SettingsPopUp(ChessProgramState *cps)
\r
10099 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
10100 EngineOptionsPopup(savedHwnd, cps);
\r
10103 int flock(int fid, int code)
\r
10105 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
10107 ov.hEvent = NULL;
\r
10109 ov.OffsetHigh = 0;
\r
10111 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
10113 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
10114 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
10115 default: return -1;
\r
10124 static char col[8][20];
\r
10125 COLORREF color = *(COLORREF *) colorVariable[n];
\r
10127 snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
10132 ActivateTheme (int new)
\r
10133 { // Redo initialization of features depending on options that can occur in themes
\r
10135 if(new) InitDrawingColors();
\r
10136 fontBitmapSquareSize = 0; // request creation of new font pieces
\r
10137 InitDrawingSizes(boardSize, 0);
\r
10138 InvalidateRect(hwndMain, NULL, TRUE);
\r