2 * WinBoard.c -- Windows NT front end to XBoard
\r
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
\r
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
\r
8 * 2007, 2008, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
\r
10 * Enhancements Copyright 2005 Alessandro Scotti
\r
12 * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,
\r
13 * which was written and is copyrighted by Wayne Christopher.
\r
15 * The following terms apply to Digital Equipment Corporation's copyright
\r
16 * interest in XBoard:
\r
17 * ------------------------------------------------------------------------
\r
18 * All Rights Reserved
\r
20 * Permission to use, copy, modify, and distribute this software and its
\r
21 * documentation for any purpose and without fee is hereby granted,
\r
22 * provided that the above copyright notice appear in all copies and that
\r
23 * both that copyright notice and this permission notice appear in
\r
24 * supporting documentation, and that the name of Digital not be
\r
25 * used in advertising or publicity pertaining to distribution of the
\r
26 * software without specific, written prior permission.
\r
28 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
\r
29 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
\r
30 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
\r
31 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
\r
32 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
\r
33 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
\r
35 * ------------------------------------------------------------------------
\r
37 * The following terms apply to the enhanced version of XBoard
\r
38 * distributed by the Free Software Foundation:
\r
39 * ------------------------------------------------------------------------
\r
41 * GNU XBoard is free software: you can redistribute it and/or modify
\r
42 * it under the terms of the GNU General Public License as published by
\r
43 * the Free Software Foundation, either version 3 of the License, or (at
\r
44 * your option) any later version.
\r
46 * GNU XBoard is distributed in the hope that it will be useful, but
\r
47 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
48 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
49 * General Public License for more details.
\r
51 * You should have received a copy of the GNU General Public License
\r
52 * along with this program. If not, see http://www.gnu.org/licenses/. *
\r
54 *------------------------------------------------------------------------
\r
55 ** See the file ChangeLog for a revision history. */
\r
59 #include <windows.h>
\r
60 #include <winuser.h>
\r
61 #include <winsock.h>
\r
62 #include <commctrl.h>
\r
68 #include <sys/stat.h>
\r
71 #include <commdlg.h>
\r
73 #include <richedit.h>
\r
74 #include <mmsystem.h>
\r
84 #include "frontend.h"
\r
85 #include "backend.h"
\r
86 #include "winboard.h"
\r
88 #include "wclipbrd.h"
\r
89 #include "woptions.h"
\r
90 #include "wsockerr.h"
\r
91 #include "defaults.h"
\r
96 #define DATADIR "~~"
\r
98 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
\r
100 int myrandom(void);
\r
101 void mysrandom(unsigned int seed);
\r
103 extern int whiteFlag, blackFlag;
\r
104 Boolean flipClock = FALSE;
\r
105 extern HANDLE chatHandle[];
\r
106 extern enum ICS_TYPE ics_type;
\r
108 int MySearchPath P((char *installDir, char *name, char *fullname));
\r
109 int MyGetFullPathName P((char *name, char *fullname));
\r
110 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
111 VOID NewVariantPopup(HWND hwnd);
\r
112 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
113 /*char*/int promoChar));
\r
114 void DisplayMove P((int moveNumber));
\r
115 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, IDOK, IDCANCEL },
\r
264 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
265 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
266 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
267 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
268 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
269 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
270 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
271 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
272 { ABOUTBOX2, IDC_ChessBoard },
\r
273 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
274 OPT_GameListClose, IDC_GameListDoFilter },
\r
275 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
276 { DLG_Error, IDOK },
\r
277 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
278 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
279 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
280 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
281 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
282 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
283 { DLG_IndexNumber, IDC_Index },
\r
284 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
285 { DLG_TypeInName, IDOK, IDCANCEL },
\r
286 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
287 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
288 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
289 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
290 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
291 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
292 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
293 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
294 OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove },
\r
295 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
296 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
297 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
298 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
299 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
300 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
301 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
302 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
303 GPB_General, GPB_Alarm, OPT_AutoCreate },
\r
304 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
305 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
306 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
307 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
308 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
309 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
310 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
311 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid },
\r
312 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
313 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
314 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
315 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
316 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
317 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
318 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,
\r
319 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
320 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
321 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
322 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
323 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,
\r
324 OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7,
\r
325 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
326 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
327 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
328 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
329 { DLG_MoveHistory },
\r
330 { DLG_EvalGraph },
\r
331 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
332 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
333 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
334 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
335 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
336 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
337 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
338 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
339 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
343 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];
\r
344 static int lastChecked;
\r
345 static char oldLanguage[MSG_SIZ], *menuText[10][30];
\r
346 extern int tinyLayout;
\r
347 extern char * menuBarText[][10];
\r
350 LoadLanguageFile(char *name)
\r
351 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
353 int i=0, j=0, n=0, k;
\r
356 if(!name || name[0] == NULLCHAR) return;
\r
357 snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension
\r
358 appData.language = oldLanguage;
\r
359 if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on
\r
360 if((f = fopen(buf, "r")) == NULL) return;
\r
361 while((k = fgetc(f)) != EOF) {
\r
362 if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }
\r
363 languageBuf[i] = k;
\r
365 if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {
\r
367 if(p = strstr(languageBuf + n + 1, "\" === \"")) {
\r
368 if(p > languageBuf+n+2 && p+8 < languageBuf+i) {
\r
369 if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }
\r
370 english[j] = languageBuf + n + 1; *p = 0;
\r
371 foreign[j++] = p + 7; languageBuf[i-1] = 0;
\r
372 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);
\r
377 } else if(i > 0 && languageBuf[i-1] == '\\') {
\r
379 case 'n': k = '\n'; break;
\r
380 case 'r': k = '\r'; break;
\r
381 case 't': k = '\t'; break;
\r
383 languageBuf[--i] = k;
\r
388 barbaric = (j != 0);
\r
389 safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );
\r
394 { // return the translation of the given string
\r
395 // efficiency can be improved a lot...
\r
397 static char buf[MSG_SIZ];
\r
398 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
399 if(!barbaric) return s;
\r
400 if(!s) return ""; // sanity
\r
401 while(english[i]) {
\r
402 if(!strcmp(s, english[i])) return foreign[i];
\r
403 if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending
\r
404 snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion
\r
413 Translate(HWND hDlg, int dialogID)
\r
414 { // translate all text items in the given dialog
\r
416 char buf[MSG_SIZ], *s;
\r
417 if(!barbaric) return;
\r
418 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
419 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
420 GetWindowText( hDlg, buf, MSG_SIZ );
\r
422 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
423 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
424 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
425 if(strlen(buf) == 0) continue;
\r
427 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
432 TranslateOneMenu(int i, HMENU subMenu)
\r
435 static MENUITEMINFO info;
\r
437 info.cbSize = sizeof(MENUITEMINFO);
\r
438 info.fMask = MIIM_STATE | MIIM_TYPE;
\r
439 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
441 info.dwTypeData = buf;
\r
442 info.cch = sizeof(buf);
\r
443 GetMenuItemInfo(subMenu, j, TRUE, &info);
\r
445 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );
\r
446 else menuText[i][j] = strdup(buf); // remember original on first change
\r
448 if(buf[0] == NULLCHAR) continue;
\r
449 info.dwTypeData = T_(buf);
\r
450 info.cch = strlen(buf)+1;
\r
451 SetMenuItemInfo(subMenu, j, TRUE, &info);
\r
457 TranslateMenus(int addLanguage)
\r
460 WIN32_FIND_DATA fileData;
\r
462 #define IDM_English 1970
\r
464 HMENU mainMenu = GetMenu(hwndMain);
\r
465 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
466 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
467 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
468 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
469 TranslateOneMenu(i, subMenu);
\r
471 DrawMenuBar(hwndMain);
\r
474 if(!addLanguage) return;
\r
475 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
476 HMENU mainMenu = GetMenu(hwndMain);
\r
477 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
478 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
479 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
480 i = 0; lastChecked = IDM_English;
\r
482 char *p, *q = fileData.cFileName;
\r
483 int checkFlag = MF_UNCHECKED;
\r
484 languageFile[i] = strdup(q);
\r
485 if(barbaric && !strcmp(oldLanguage, q)) {
\r
486 checkFlag = MF_CHECKED;
\r
487 lastChecked = IDM_English + i + 1;
\r
488 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
490 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
491 p = strstr(fileData.cFileName, ".lng");
\r
493 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
494 } while(FindNextFile(hFind, &fileData));
\r
501 #define IDM_RecentEngines 3000
\r
504 RecentEngineMenu (char *s)
\r
506 if(appData.icsActive) return;
\r
507 if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty
\r
508 HMENU mainMenu = GetMenu(hwndMain);
\r
509 HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu
\r
510 int i=IDM_RecentEngines;
\r
511 recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu
\r
512 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
514 char *p = strchr(s, '\n');
\r
515 if(p == NULL) return; // malformed!
\r
517 AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);
\r
531 int cliWidth, cliHeight;
\r
534 SizeInfo sizeInfo[] =
\r
536 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
537 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
538 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
539 { "petite", 33, 1, 1, 1, 0, 0 },
\r
540 { "slim", 37, 2, 1, 0, 0, 0 },
\r
541 { "small", 40, 2, 1, 0, 0, 0 },
\r
542 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
543 { "middling", 49, 2, 0, 0, 0, 0 },
\r
544 { "average", 54, 2, 0, 0, 0, 0 },
\r
545 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
546 { "medium", 64, 3, 0, 0, 0, 0 },
\r
547 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
548 { "large", 80, 3, 0, 0, 0, 0 },
\r
549 { "big", 87, 3, 0, 0, 0, 0 },
\r
550 { "huge", 95, 3, 0, 0, 0, 0 },
\r
551 { "giant", 108, 3, 0, 0, 0, 0 },
\r
552 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
553 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
554 { NULL, 0, 0, 0, 0, 0, 0 }
\r
557 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
558 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
560 { 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
561 { 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
562 { 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
563 { 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
564 { 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
565 { 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
566 { 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
567 { 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
568 { 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
569 { 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
570 { 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
571 { 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
572 { 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
573 { 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
574 { 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
575 { 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
576 { 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
577 { 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
580 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
589 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
590 #define N_BUTTONS 5
\r
592 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
594 {"<<", IDM_ToStart, NULL, NULL},
\r
595 {"<", IDM_Backward, NULL, NULL},
\r
596 {"P", IDM_Pause, NULL, NULL},
\r
597 {">", IDM_Forward, NULL, NULL},
\r
598 {">>", IDM_ToEnd, NULL, NULL},
\r
601 int tinyLayout = 0, smallLayout = 0;
\r
602 #define MENU_BAR_ITEMS 9
\r
603 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
604 { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },
\r
605 { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },
\r
609 MySound sounds[(int)NSoundClasses];
\r
610 MyTextAttribs textAttribs[(int)NColorClasses];
\r
612 MyColorizeAttribs colorizeAttribs[] = {
\r
613 { (COLORREF)0, 0, N_("Shout Text") },
\r
614 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
615 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
616 { (COLORREF)0, 0, N_("Channel Text") },
\r
617 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
618 { (COLORREF)0, 0, N_("Tell Text") },
\r
619 { (COLORREF)0, 0, N_("Challenge Text") },
\r
620 { (COLORREF)0, 0, N_("Request Text") },
\r
621 { (COLORREF)0, 0, N_("Seek Text") },
\r
622 { (COLORREF)0, 0, N_("Normal Text") },
\r
623 { (COLORREF)0, 0, N_("None") }
\r
628 static char *commentTitle;
\r
629 static char *commentText;
\r
630 static int commentIndex;
\r
631 static Boolean editComment = FALSE;
\r
634 char errorTitle[MSG_SIZ];
\r
635 char errorMessage[2*MSG_SIZ];
\r
636 HWND errorDialog = NULL;
\r
637 BOOLEAN moveErrorMessageUp = FALSE;
\r
638 BOOLEAN consoleEcho = TRUE;
\r
639 CHARFORMAT consoleCF;
\r
640 COLORREF consoleBackgroundColor;
\r
642 char *programVersion;
\r
648 typedef int CPKind;
\r
657 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
660 #define INPUT_SOURCE_BUF_SIZE 4096
\r
662 typedef struct _InputSource {
\r
669 char buf[INPUT_SOURCE_BUF_SIZE];
\r
673 InputCallback func;
\r
674 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
678 InputSource *consoleInputSource;
\r
683 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
684 VOID ConsoleCreate();
\r
686 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
687 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
688 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
689 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
691 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
692 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
693 void ParseIcsTextMenu(char *icsTextMenuString);
\r
694 VOID PopUpNameDialog(char firstchar);
\r
695 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
699 int GameListOptions();
\r
701 int dummy; // [HGM] for obsolete args
\r
703 HWND hwndMain = NULL; /* root window*/
\r
704 HWND hwndConsole = NULL;
\r
705 HWND commentDialog = NULL;
\r
706 HWND moveHistoryDialog = NULL;
\r
707 HWND evalGraphDialog = NULL;
\r
708 HWND engineOutputDialog = NULL;
\r
709 HWND gameListDialog = NULL;
\r
710 HWND editTagsDialog = NULL;
\r
712 int commentUp = FALSE;
\r
714 WindowPlacement wpMain;
\r
715 WindowPlacement wpConsole;
\r
716 WindowPlacement wpComment;
\r
717 WindowPlacement wpMoveHistory;
\r
718 WindowPlacement wpEvalGraph;
\r
719 WindowPlacement wpEngineOutput;
\r
720 WindowPlacement wpGameList;
\r
721 WindowPlacement wpTags;
\r
723 VOID EngineOptionsPopup(); // [HGM] settings
\r
725 VOID GothicPopUp(char *title, VariantClass variant);
\r
727 * Setting "frozen" should disable all user input other than deleting
\r
728 * the window. We do this while engines are initializing themselves.
\r
730 static int frozen = 0;
\r
731 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
737 if (frozen) return;
\r
739 hmenu = GetMenu(hwndMain);
\r
740 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
741 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
743 DrawMenuBar(hwndMain);
\r
746 /* Undo a FreezeUI */
\r
752 if (!frozen) return;
\r
754 hmenu = GetMenu(hwndMain);
\r
755 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
756 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
758 DrawMenuBar(hwndMain);
\r
761 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
763 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
769 #define JAWS_ALT_INTERCEPT
\r
770 #define JAWS_KBUP_NAVIGATION
\r
771 #define JAWS_KBDOWN_NAVIGATION
\r
772 #define JAWS_MENU_ITEMS
\r
773 #define JAWS_SILENCE
\r
774 #define JAWS_REPLAY
\r
776 #define JAWS_COPYRIGHT
\r
777 #define JAWS_DELETE(X) X
\r
778 #define SAYMACHINEMOVE()
\r
782 /*---------------------------------------------------------------------------*\
\r
786 \*---------------------------------------------------------------------------*/
\r
789 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
790 LPSTR lpCmdLine, int nCmdShow)
\r
793 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
794 // INITCOMMONCONTROLSEX ex;
\r
798 LoadLibrary("RICHED32.DLL");
\r
799 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
801 if (!InitApplication(hInstance)) {
\r
804 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
811 // InitCommonControlsEx(&ex);
\r
812 InitCommonControls();
\r
814 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
815 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
816 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
818 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
820 while (GetMessage(&msg, /* message structure */
\r
821 NULL, /* handle of window receiving the message */
\r
822 0, /* lowest message to examine */
\r
823 0)) /* highest message to examine */
\r
826 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
827 // [HGM] navigate: switch between all windows with tab
\r
828 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
829 int i, currentElement = 0;
\r
831 // first determine what element of the chain we come from (if any)
\r
832 if(appData.icsActive) {
\r
833 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
834 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
836 if(engineOutputDialog && EngineOutputIsUp()) {
\r
837 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
838 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
840 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
841 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
843 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
844 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
845 if(msg.hwnd == e1) currentElement = 2; else
\r
846 if(msg.hwnd == e2) currentElement = 3; else
\r
847 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
848 if(msg.hwnd == mh) currentElement = 4; else
\r
849 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
850 if(msg.hwnd == hText) currentElement = 5; else
\r
851 if(msg.hwnd == hInput) currentElement = 6; else
\r
852 for (i = 0; i < N_BUTTONS; i++) {
\r
853 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
856 // determine where to go to
\r
857 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
859 currentElement = (currentElement + direction) % 7;
\r
860 switch(currentElement) {
\r
862 h = hwndMain; break; // passing this case always makes the loop exit
\r
864 h = buttonDesc[0].hwnd; break; // could be NULL
\r
866 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
869 if(!EngineOutputIsUp()) continue;
\r
872 if(!MoveHistoryIsUp()) continue;
\r
874 // case 6: // input to eval graph does not seem to get here!
\r
875 // if(!EvalGraphIsUp()) continue;
\r
876 // h = evalGraphDialog; break;
\r
878 if(!appData.icsActive) continue;
\r
882 if(!appData.icsActive) continue;
\r
888 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
889 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
892 continue; // this message now has been processed
\r
896 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
897 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
898 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
899 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
900 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
901 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
902 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
903 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
904 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
905 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
906 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
907 for(i=0; i<MAX_CHAT; i++)
\r
908 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
911 if(done) continue; // [HGM] chat: end patch
\r
912 TranslateMessage(&msg); /* Translates virtual key codes */
\r
913 DispatchMessage(&msg); /* Dispatches message to window */
\r
918 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
921 /*---------------------------------------------------------------------------*\
\r
923 * Initialization functions
\r
925 \*---------------------------------------------------------------------------*/
\r
929 { // update user logo if necessary
\r
930 static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;
\r
932 if(appData.autoLogo) {
\r
933 curName = UserName();
\r
934 if(strcmp(curName, oldUserName)) {
\r
935 GetCurrentDirectory(MSG_SIZ, dir);
\r
936 SetCurrentDirectory(installDir);
\r
937 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
938 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
939 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
940 if(userLogo == NULL)
\r
941 userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
942 SetCurrentDirectory(dir); /* return to prev directory */
\r
948 InitApplication(HINSTANCE hInstance)
\r
952 /* Fill in window class structure with parameters that describe the */
\r
955 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
956 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
957 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
958 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
959 wc.hInstance = hInstance; /* Owner of this class */
\r
960 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
961 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
962 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
963 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
964 wc.lpszClassName = szAppName; /* Name to register as */
\r
966 /* Register the window class and return success/failure code. */
\r
967 if (!RegisterClass(&wc)) return FALSE;
\r
969 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
970 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
972 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
973 wc.hInstance = hInstance;
\r
974 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
975 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
976 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
977 wc.lpszMenuName = NULL;
\r
978 wc.lpszClassName = szConsoleName;
\r
980 if (!RegisterClass(&wc)) return FALSE;
\r
985 /* Set by InitInstance, used by EnsureOnScreen */
\r
986 int screenHeight, screenWidth;
\r
989 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
991 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
992 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
993 if (*x > screenWidth - 32) *x = 0;
\r
994 if (*y > screenHeight - 32) *y = 0;
\r
995 if (*x < minX) *x = minX;
\r
996 if (*y < minY) *y = minY;
\r
1000 LoadLogo(ChessProgramState *cps, int n, Boolean ics)
\r
1002 char buf[MSG_SIZ], dir[MSG_SIZ];
\r
1003 GetCurrentDirectory(MSG_SIZ, dir);
\r
1004 SetCurrentDirectory(installDir);
\r
1005 if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {
\r
1006 cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1008 if (cps->programLogo == NULL && appData.debugMode) {
\r
1009 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );
\r
1011 } else if(appData.autoLogo) {
\r
1012 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
\r
1013 char *opponent = "";
\r
1014 if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;
\r
1015 if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;
\r
1016 sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);
\r
1017 if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {
\r
1018 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
1019 cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1022 if(appData.directory[n] && appData.directory[n][0]) {
\r
1023 SetCurrentDirectory(appData.directory[n]);
\r
1024 cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1027 SetCurrentDirectory(dir); /* return to prev directory */
\r
1033 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
1034 backTextureSquareSize = 0; // kludge to force recalculation of texturemode
\r
1036 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1037 if(liteBackTexture) DeleteObject(liteBackTexture);
\r
1038 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1039 liteBackTextureMode = appData.liteBackTextureMode;
\r
1041 if (liteBackTexture == NULL && appData.debugMode) {
\r
1042 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1046 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1047 if(darkBackTexture) DeleteObject(darkBackTexture);
\r
1048 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1049 darkBackTextureMode = appData.darkBackTextureMode;
\r
1051 if (darkBackTexture == NULL && appData.debugMode) {
\r
1052 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1058 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
1060 HWND hwnd; /* Main window handle. */
\r
1062 WINDOWPLACEMENT wp;
\r
1065 hInst = hInstance; /* Store instance handle in our global variable */
\r
1066 programName = szAppName;
\r
1068 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
1069 *filepart = NULLCHAR;
\r
1070 SetCurrentDirectory(installDir);
\r
1072 GetCurrentDirectory(MSG_SIZ, installDir);
\r
1074 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
1075 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
1076 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
1077 /* xboard, and older WinBoards, controlled the move sound with the
\r
1078 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
1079 always turn the option on (so that the backend will call us),
\r
1080 then let the user turn the sound off by setting it to silence if
\r
1081 desired. To accommodate old winboard.ini files saved by old
\r
1082 versions of WinBoard, we also turn off the sound if the option
\r
1083 was initially set to false. [HGM] taken out of InitAppData */
\r
1084 if (!appData.ringBellAfterMoves) {
\r
1085 sounds[(int)SoundMove].name = strdup("");
\r
1086 appData.ringBellAfterMoves = TRUE;
\r
1088 if (appData.debugMode) {
\r
1089 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
1090 setbuf(debugFP, NULL);
\r
1093 LoadLanguageFile(appData.language);
\r
1097 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
1098 // InitEngineUCI( installDir, &second );
\r
1100 /* Create a main window for this application instance. */
\r
1101 hwnd = CreateWindow(szAppName, szTitle,
\r
1102 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
1103 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
1104 NULL, NULL, hInstance, NULL);
\r
1107 /* If window could not be created, return "failure" */
\r
1112 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1113 LoadLogo(&first, 0, FALSE);
\r
1114 LoadLogo(&second, 1, appData.icsActive);
\r
1118 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1119 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1120 iconCurrent = iconWhite;
\r
1121 InitDrawingColors();
\r
1122 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1123 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1124 InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args
\r
1125 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1126 /* Compute window size for each board size, and use the largest
\r
1127 size that fits on this screen as the default. */
\r
1128 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1129 if (boardSize == (BoardSize)-1 &&
\r
1130 winH <= screenHeight
\r
1131 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1132 && winW <= screenWidth) {
\r
1133 boardSize = (BoardSize)ibs;
\r
1137 InitDrawingSizes(boardSize, 0);
\r
1138 RecentEngineMenu(appData.recentEngineList);
\r
1140 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1142 /* [AS] Load textures if specified */
\r
1145 mysrandom( (unsigned) time(NULL) );
\r
1147 /* [AS] Restore layout */
\r
1148 if( wpMoveHistory.visible ) {
\r
1149 MoveHistoryPopUp();
\r
1152 if( wpEvalGraph.visible ) {
\r
1156 if( wpEngineOutput.visible ) {
\r
1157 EngineOutputPopUp();
\r
1160 /* Make the window visible; update its client area; and return "success" */
\r
1161 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1162 wp.length = sizeof(WINDOWPLACEMENT);
\r
1164 wp.showCmd = nCmdShow;
\r
1165 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1166 wp.rcNormalPosition.left = wpMain.x;
\r
1167 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1168 wp.rcNormalPosition.top = wpMain.y;
\r
1169 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1170 SetWindowPlacement(hwndMain, &wp);
\r
1172 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1174 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1175 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1177 if (hwndConsole) {
\r
1179 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1180 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1182 ShowWindow(hwndConsole, nCmdShow);
\r
1183 SetActiveWindow(hwndConsole);
\r
1185 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1186 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1195 HMENU hmenu = GetMenu(hwndMain);
\r
1197 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1198 MF_BYCOMMAND|((appData.icsActive &&
\r
1199 *appData.icsCommPort != NULLCHAR) ?
\r
1200 MF_ENABLED : MF_GRAYED));
\r
1201 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1202 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1203 MF_CHECKED : MF_UNCHECKED));
\r
1206 //---------------------------------------------------------------------------------------------------------
\r
1208 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1209 #define XBOARD FALSE
\r
1211 #define OPTCHAR "/"
\r
1212 #define SEPCHAR "="
\r
1213 #define TOPLEVEL 0
\r
1217 // front-end part of option handling
\r
1220 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1222 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1223 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1226 lf->lfEscapement = 0;
\r
1227 lf->lfOrientation = 0;
\r
1228 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1229 lf->lfItalic = mfp->italic;
\r
1230 lf->lfUnderline = mfp->underline;
\r
1231 lf->lfStrikeOut = mfp->strikeout;
\r
1232 lf->lfCharSet = mfp->charset;
\r
1233 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1234 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1235 lf->lfQuality = DEFAULT_QUALITY;
\r
1236 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1237 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1241 CreateFontInMF(MyFont *mf)
\r
1243 LFfromMFP(&mf->lf, &mf->mfp);
\r
1244 if (mf->hf) DeleteObject(mf->hf);
\r
1245 mf->hf = CreateFontIndirect(&mf->lf);
\r
1248 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1250 colorVariable[] = {
\r
1251 &whitePieceColor,
\r
1252 &blackPieceColor,
\r
1253 &lightSquareColor,
\r
1254 &darkSquareColor,
\r
1255 &highlightSquareColor,
\r
1256 &premoveHighlightColor,
\r
1258 &consoleBackgroundColor,
\r
1259 &appData.fontForeColorWhite,
\r
1260 &appData.fontBackColorWhite,
\r
1261 &appData.fontForeColorBlack,
\r
1262 &appData.fontBackColorBlack,
\r
1263 &appData.evalHistColorWhite,
\r
1264 &appData.evalHistColorBlack,
\r
1265 &appData.highlightArrowColor,
\r
1268 /* Command line font name parser. NULL name means do nothing.
\r
1269 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1270 For backward compatibility, syntax without the colon is also
\r
1271 accepted, but font names with digits in them won't work in that case.
\r
1274 ParseFontName(char *name, MyFontParams *mfp)
\r
1277 if (name == NULL) return;
\r
1279 q = strchr(p, ':');
\r
1281 if (q - p >= sizeof(mfp->faceName))
\r
1282 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1283 memcpy(mfp->faceName, p, q - p);
\r
1284 mfp->faceName[q - p] = NULLCHAR;
\r
1287 q = mfp->faceName;
\r
1289 while (*p && !isdigit(*p)) {
\r
1291 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1292 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1294 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1297 if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);
\r
1298 mfp->pointSize = (float) atof(p);
\r
1299 mfp->bold = (strchr(p, 'b') != NULL);
\r
1300 mfp->italic = (strchr(p, 'i') != NULL);
\r
1301 mfp->underline = (strchr(p, 'u') != NULL);
\r
1302 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1303 mfp->charset = DEFAULT_CHARSET;
\r
1304 q = strchr(p, 'c');
\r
1306 mfp->charset = (BYTE) atoi(q+1);
\r
1310 ParseFont(char *name, int number)
\r
1311 { // wrapper to shield back-end from 'font'
\r
1312 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1317 { // in WB we have a 2D array of fonts; this initializes their description
\r
1319 /* Point font array elements to structures and
\r
1320 parse default font names */
\r
1321 for (i=0; i<NUM_FONTS; i++) {
\r
1322 for (j=0; j<NUM_SIZES; j++) {
\r
1323 font[j][i] = &fontRec[j][i];
\r
1324 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1331 { // here we create the actual fonts from the selected descriptions
\r
1333 for (i=0; i<NUM_FONTS; i++) {
\r
1334 for (j=0; j<NUM_SIZES; j++) {
\r
1335 CreateFontInMF(font[j][i]);
\r
1339 /* Color name parser.
\r
1340 X version accepts X color names, but this one
\r
1341 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1343 ParseColorName(char *name)
\r
1345 int red, green, blue, count;
\r
1346 char buf[MSG_SIZ];
\r
1348 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1350 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1351 &red, &green, &blue);
\r
1354 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1355 DisplayError(buf, 0);
\r
1356 return RGB(0, 0, 0);
\r
1358 return PALETTERGB(red, green, blue);
\r
1362 ParseColor(int n, char *name)
\r
1363 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1364 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1368 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1370 char *e = argValue;
\r
1374 if (*e == 'b') eff |= CFE_BOLD;
\r
1375 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1376 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1377 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1378 else if (*e == '#' || isdigit(*e)) break;
\r
1382 *color = ParseColorName(e);
\r
1386 ParseTextAttribs(ColorClass cc, char *s)
\r
1387 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1388 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1389 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1393 ParseBoardSize(void *addr, char *name)
\r
1394 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1395 BoardSize bs = SizeTiny;
\r
1396 while (sizeInfo[bs].name != NULL) {
\r
1397 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1398 *(BoardSize *)addr = bs;
\r
1403 ExitArgError(_("Unrecognized board size value"), name, TRUE);
\r
1408 { // [HGM] import name from appData first
\r
1411 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1412 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1413 textAttribs[cc].sound.data = NULL;
\r
1414 MyLoadSound(&textAttribs[cc].sound);
\r
1416 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1417 textAttribs[cc].sound.name = strdup("");
\r
1418 textAttribs[cc].sound.data = NULL;
\r
1420 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1421 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1422 sounds[sc].data = NULL;
\r
1423 MyLoadSound(&sounds[sc]);
\r
1428 SetCommPortDefaults()
\r
1430 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1431 dcb.DCBlength = sizeof(DCB);
\r
1432 dcb.BaudRate = 9600;
\r
1433 dcb.fBinary = TRUE;
\r
1434 dcb.fParity = FALSE;
\r
1435 dcb.fOutxCtsFlow = FALSE;
\r
1436 dcb.fOutxDsrFlow = FALSE;
\r
1437 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1438 dcb.fDsrSensitivity = FALSE;
\r
1439 dcb.fTXContinueOnXoff = TRUE;
\r
1440 dcb.fOutX = FALSE;
\r
1442 dcb.fNull = FALSE;
\r
1443 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1444 dcb.fAbortOnError = FALSE;
\r
1446 dcb.Parity = SPACEPARITY;
\r
1447 dcb.StopBits = ONESTOPBIT;
\r
1450 // [HGM] args: these three cases taken out to stay in front-end
\r
1452 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1453 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1454 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1455 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1457 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1458 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1459 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1460 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1461 ad->argName, mfp->faceName, mfp->pointSize,
\r
1462 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1463 mfp->bold ? "b" : "",
\r
1464 mfp->italic ? "i" : "",
\r
1465 mfp->underline ? "u" : "",
\r
1466 mfp->strikeout ? "s" : "",
\r
1467 (int)mfp->charset);
\r
1473 { // [HGM] copy the names from the internal WB variables to appData
\r
1476 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1477 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1478 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1479 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1483 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1484 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1485 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1486 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1487 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1488 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1489 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1490 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1491 (ta->effects) ? " " : "",
\r
1492 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1496 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1497 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1498 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1499 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1500 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1504 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1505 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1506 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1510 ParseCommPortSettings(char *s)
\r
1511 { // wrapper to keep dcb from back-end
\r
1512 ParseCommSettings(s, &dcb);
\r
1517 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1518 GetActualPlacement(hwndMain, &wpMain);
\r
1519 GetActualPlacement(hwndConsole, &wpConsole);
\r
1520 GetActualPlacement(commentDialog, &wpComment);
\r
1521 GetActualPlacement(editTagsDialog, &wpTags);
\r
1522 GetActualPlacement(gameListDialog, &wpGameList);
\r
1523 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1524 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1525 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1529 PrintCommPortSettings(FILE *f, char *name)
\r
1530 { // wrapper to shield back-end from DCB
\r
1531 PrintCommSettings(f, name, &dcb);
\r
1535 MySearchPath(char *installDir, char *name, char *fullname)
\r
1537 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1538 if(name[0]== '%') {
\r
1539 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1540 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1541 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1542 *strchr(buf, '%') = 0;
\r
1543 strcat(fullname, getenv(buf));
\r
1544 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1546 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1547 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1548 return (int) strlen(fullname);
\r
1550 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1554 MyGetFullPathName(char *name, char *fullname)
\r
1557 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1562 { // [HGM] args: allows testing if main window is realized from back-end
\r
1563 return hwndMain != NULL;
\r
1567 PopUpStartupDialog()
\r
1571 LoadLanguageFile(appData.language);
\r
1572 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1573 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1574 FreeProcInstance(lpProc);
\r
1577 /*---------------------------------------------------------------------------*\
\r
1579 * GDI board drawing routines
\r
1581 \*---------------------------------------------------------------------------*/
\r
1583 /* [AS] Draw square using background texture */
\r
1584 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1589 return; /* Should never happen! */
\r
1592 SetGraphicsMode( dst, GM_ADVANCED );
\r
1599 /* X reflection */
\r
1604 x.eDx = (FLOAT) dw + dx - 1;
\r
1607 SetWorldTransform( dst, &x );
\r
1610 /* Y reflection */
\r
1616 x.eDy = (FLOAT) dh + dy - 1;
\r
1618 SetWorldTransform( dst, &x );
\r
1626 x.eDx = (FLOAT) dx;
\r
1627 x.eDy = (FLOAT) dy;
\r
1630 SetWorldTransform( dst, &x );
\r
1634 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1642 SetWorldTransform( dst, &x );
\r
1644 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1647 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1649 PM_WP = (int) WhitePawn,
\r
1650 PM_WN = (int) WhiteKnight,
\r
1651 PM_WB = (int) WhiteBishop,
\r
1652 PM_WR = (int) WhiteRook,
\r
1653 PM_WQ = (int) WhiteQueen,
\r
1654 PM_WF = (int) WhiteFerz,
\r
1655 PM_WW = (int) WhiteWazir,
\r
1656 PM_WE = (int) WhiteAlfil,
\r
1657 PM_WM = (int) WhiteMan,
\r
1658 PM_WO = (int) WhiteCannon,
\r
1659 PM_WU = (int) WhiteUnicorn,
\r
1660 PM_WH = (int) WhiteNightrider,
\r
1661 PM_WA = (int) WhiteAngel,
\r
1662 PM_WC = (int) WhiteMarshall,
\r
1663 PM_WAB = (int) WhiteCardinal,
\r
1664 PM_WD = (int) WhiteDragon,
\r
1665 PM_WL = (int) WhiteLance,
\r
1666 PM_WS = (int) WhiteCobra,
\r
1667 PM_WV = (int) WhiteFalcon,
\r
1668 PM_WSG = (int) WhiteSilver,
\r
1669 PM_WG = (int) WhiteGrasshopper,
\r
1670 PM_WK = (int) WhiteKing,
\r
1671 PM_BP = (int) BlackPawn,
\r
1672 PM_BN = (int) BlackKnight,
\r
1673 PM_BB = (int) BlackBishop,
\r
1674 PM_BR = (int) BlackRook,
\r
1675 PM_BQ = (int) BlackQueen,
\r
1676 PM_BF = (int) BlackFerz,
\r
1677 PM_BW = (int) BlackWazir,
\r
1678 PM_BE = (int) BlackAlfil,
\r
1679 PM_BM = (int) BlackMan,
\r
1680 PM_BO = (int) BlackCannon,
\r
1681 PM_BU = (int) BlackUnicorn,
\r
1682 PM_BH = (int) BlackNightrider,
\r
1683 PM_BA = (int) BlackAngel,
\r
1684 PM_BC = (int) BlackMarshall,
\r
1685 PM_BG = (int) BlackGrasshopper,
\r
1686 PM_BAB = (int) BlackCardinal,
\r
1687 PM_BD = (int) BlackDragon,
\r
1688 PM_BL = (int) BlackLance,
\r
1689 PM_BS = (int) BlackCobra,
\r
1690 PM_BV = (int) BlackFalcon,
\r
1691 PM_BSG = (int) BlackSilver,
\r
1692 PM_BK = (int) BlackKing
\r
1695 static HFONT hPieceFont = NULL;
\r
1696 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1697 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1698 static int fontBitmapSquareSize = 0;
\r
1699 static char pieceToFontChar[(int) EmptySquare] =
\r
1700 { 'p', 'n', 'b', 'r', 'q',
\r
1701 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1702 'k', 'o', 'm', 'v', 't', 'w',
\r
1703 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1706 extern BOOL SetCharTable( char *table, const char * map );
\r
1707 /* [HGM] moved to backend.c */
\r
1709 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1712 BYTE r1 = GetRValue( color );
\r
1713 BYTE g1 = GetGValue( color );
\r
1714 BYTE b1 = GetBValue( color );
\r
1720 /* Create a uniform background first */
\r
1721 hbrush = CreateSolidBrush( color );
\r
1722 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1723 FillRect( hdc, &rc, hbrush );
\r
1724 DeleteObject( hbrush );
\r
1727 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1728 int steps = squareSize / 2;
\r
1731 for( i=0; i<steps; i++ ) {
\r
1732 BYTE r = r1 - (r1-r2) * i / steps;
\r
1733 BYTE g = g1 - (g1-g2) * i / steps;
\r
1734 BYTE b = b1 - (b1-b2) * i / steps;
\r
1736 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1737 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1738 FillRect( hdc, &rc, hbrush );
\r
1739 DeleteObject(hbrush);
\r
1742 else if( mode == 2 ) {
\r
1743 /* Diagonal gradient, good more or less for every piece */
\r
1744 POINT triangle[3];
\r
1745 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1746 HBRUSH hbrush_old;
\r
1747 int steps = squareSize;
\r
1750 triangle[0].x = squareSize - steps;
\r
1751 triangle[0].y = squareSize;
\r
1752 triangle[1].x = squareSize;
\r
1753 triangle[1].y = squareSize;
\r
1754 triangle[2].x = squareSize;
\r
1755 triangle[2].y = squareSize - steps;
\r
1757 for( i=0; i<steps; i++ ) {
\r
1758 BYTE r = r1 - (r1-r2) * i / steps;
\r
1759 BYTE g = g1 - (g1-g2) * i / steps;
\r
1760 BYTE b = b1 - (b1-b2) * i / steps;
\r
1762 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1763 hbrush_old = SelectObject( hdc, hbrush );
\r
1764 Polygon( hdc, triangle, 3 );
\r
1765 SelectObject( hdc, hbrush_old );
\r
1766 DeleteObject(hbrush);
\r
1771 SelectObject( hdc, hpen );
\r
1776 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1777 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1778 piece: follow the steps as explained below.
\r
1780 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1784 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1788 int backColor = whitePieceColor;
\r
1789 int foreColor = blackPieceColor;
\r
1791 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1792 backColor = appData.fontBackColorWhite;
\r
1793 foreColor = appData.fontForeColorWhite;
\r
1795 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1796 backColor = appData.fontBackColorBlack;
\r
1797 foreColor = appData.fontForeColorBlack;
\r
1801 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1803 hbm_old = SelectObject( hdc, hbm );
\r
1807 rc.right = squareSize;
\r
1808 rc.bottom = squareSize;
\r
1810 /* Step 1: background is now black */
\r
1811 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1813 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1815 pt.x = (squareSize - sz.cx) / 2;
\r
1816 pt.y = (squareSize - sz.cy) / 2;
\r
1818 SetBkMode( hdc, TRANSPARENT );
\r
1819 SetTextColor( hdc, chroma );
\r
1820 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1821 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1823 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1824 /* Step 3: the area outside the piece is filled with white */
\r
1825 // FloodFill( hdc, 0, 0, chroma );
\r
1826 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1827 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1828 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1829 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1830 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1832 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1833 but if the start point is not inside the piece we're lost!
\r
1834 There should be a better way to do this... if we could create a region or path
\r
1835 from the fill operation we would be fine for example.
\r
1837 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1838 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1840 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1841 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1842 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1844 SelectObject( dc2, bm2 );
\r
1845 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1846 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1847 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1848 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1849 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1852 DeleteObject( bm2 );
\r
1855 SetTextColor( hdc, 0 );
\r
1857 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1858 draw the piece again in black for safety.
\r
1860 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1862 SelectObject( hdc, hbm_old );
\r
1864 if( hPieceMask[index] != NULL ) {
\r
1865 DeleteObject( hPieceMask[index] );
\r
1868 hPieceMask[index] = hbm;
\r
1871 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1873 SelectObject( hdc, hbm );
\r
1876 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1877 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1878 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1880 SelectObject( dc1, hPieceMask[index] );
\r
1881 SelectObject( dc2, bm2 );
\r
1882 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1883 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1886 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1887 the piece background and deletes (makes transparent) the rest.
\r
1888 Thanks to that mask, we are free to paint the background with the greates
\r
1889 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1890 We use this, to make gradients and give the pieces a "roundish" look.
\r
1892 SetPieceBackground( hdc, backColor, 2 );
\r
1893 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1897 DeleteObject( bm2 );
\r
1900 SetTextColor( hdc, foreColor );
\r
1901 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1903 SelectObject( hdc, hbm_old );
\r
1905 if( hPieceFace[index] != NULL ) {
\r
1906 DeleteObject( hPieceFace[index] );
\r
1909 hPieceFace[index] = hbm;
\r
1912 static int TranslatePieceToFontPiece( int piece )
\r
1942 case BlackMarshall:
\r
1946 case BlackNightrider:
\r
1952 case BlackUnicorn:
\r
1956 case BlackGrasshopper:
\r
1968 case BlackCardinal:
\r
1975 case WhiteMarshall:
\r
1979 case WhiteNightrider:
\r
1985 case WhiteUnicorn:
\r
1989 case WhiteGrasshopper:
\r
2001 case WhiteCardinal:
\r
2010 void CreatePiecesFromFont()
\r
2013 HDC hdc_window = NULL;
\r
2019 if( fontBitmapSquareSize < 0 ) {
\r
2020 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
2024 if( !appData.useFont || appData.renderPiecesWithFont == NULL ||
\r
2025 appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
2026 fontBitmapSquareSize = -1;
\r
2030 if( fontBitmapSquareSize != squareSize ) {
\r
2031 hdc_window = GetDC( hwndMain );
\r
2032 hdc = CreateCompatibleDC( hdc_window );
\r
2034 if( hPieceFont != NULL ) {
\r
2035 DeleteObject( hPieceFont );
\r
2038 for( i=0; i<=(int)BlackKing; i++ ) {
\r
2039 hPieceMask[i] = NULL;
\r
2040 hPieceFace[i] = NULL;
\r
2046 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
2047 fontHeight = appData.fontPieceSize;
\r
2050 fontHeight = (fontHeight * squareSize) / 100;
\r
2052 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2054 lf.lfEscapement = 0;
\r
2055 lf.lfOrientation = 0;
\r
2056 lf.lfWeight = FW_NORMAL;
\r
2058 lf.lfUnderline = 0;
\r
2059 lf.lfStrikeOut = 0;
\r
2060 lf.lfCharSet = DEFAULT_CHARSET;
\r
2061 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2062 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2063 lf.lfQuality = PROOF_QUALITY;
\r
2064 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2065 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2066 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2068 hPieceFont = CreateFontIndirect( &lf );
\r
2070 if( hPieceFont == NULL ) {
\r
2071 fontBitmapSquareSize = -2;
\r
2074 /* Setup font-to-piece character table */
\r
2075 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2076 /* No (or wrong) global settings, try to detect the font */
\r
2077 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2079 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2081 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2082 /* DiagramTT* family */
\r
2083 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2085 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2086 /* Fairy symbols */
\r
2087 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2089 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2090 /* Good Companion (Some characters get warped as literal :-( */
\r
2091 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2092 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2093 SetCharTable(pieceToFontChar, s);
\r
2096 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2097 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2101 /* Create bitmaps */
\r
2102 hfont_old = SelectObject( hdc, hPieceFont );
\r
2103 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2104 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2105 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2107 SelectObject( hdc, hfont_old );
\r
2109 fontBitmapSquareSize = squareSize;
\r
2113 if( hdc != NULL ) {
\r
2117 if( hdc_window != NULL ) {
\r
2118 ReleaseDC( hwndMain, hdc_window );
\r
2123 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2125 char name[128], buf[MSG_SIZ];
\r
2127 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2128 if(appData.pieceDirectory[0]) {
\r
2130 snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);
\r
2131 res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
2132 if(res) return res;
\r
2134 if (gameInfo.event &&
\r
2135 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2136 strcmp(name, "k80s") == 0) {
\r
2137 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2139 return LoadBitmap(hinst, name);
\r
2143 /* Insert a color into the program's logical palette
\r
2144 structure. This code assumes the given color is
\r
2145 the result of the RGB or PALETTERGB macro, and it
\r
2146 knows how those macros work (which is documented).
\r
2149 InsertInPalette(COLORREF color)
\r
2151 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2153 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2154 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2155 pLogPal->palNumEntries--;
\r
2159 pe->peFlags = (char) 0;
\r
2160 pe->peRed = (char) (0xFF & color);
\r
2161 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2162 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2168 InitDrawingColors()
\r
2171 if (pLogPal == NULL) {
\r
2172 /* Allocate enough memory for a logical palette with
\r
2173 * PALETTESIZE entries and set the size and version fields
\r
2174 * of the logical palette structure.
\r
2176 pLogPal = (NPLOGPALETTE)
\r
2177 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2178 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2179 pLogPal->palVersion = 0x300;
\r
2181 pLogPal->palNumEntries = 0;
\r
2183 InsertInPalette(lightSquareColor);
\r
2184 InsertInPalette(darkSquareColor);
\r
2185 InsertInPalette(whitePieceColor);
\r
2186 InsertInPalette(blackPieceColor);
\r
2187 InsertInPalette(highlightSquareColor);
\r
2188 InsertInPalette(premoveHighlightColor);
\r
2190 /* create a logical color palette according the information
\r
2191 * in the LOGPALETTE structure.
\r
2193 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2195 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2196 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2197 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2198 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2199 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2200 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2201 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2202 for(i=0; i<8;i++) markerBrush[i] = CreateSolidBrush(markerColor[i]); // [HGM] markers
\r
2204 /* [AS] Force rendering of the font-based pieces */
\r
2205 if( fontBitmapSquareSize > 0 ) {
\r
2206 fontBitmapSquareSize = 0;
\r
2212 BoardWidth(int boardSize, int n)
\r
2213 { /* [HGM] argument n added to allow different width and height */
\r
2214 int lineGap = sizeInfo[boardSize].lineGap;
\r
2216 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2217 lineGap = appData.overrideLineGap;
\r
2220 return (n + 1) * lineGap +
\r
2221 n * sizeInfo[boardSize].squareSize;
\r
2224 /* Respond to board resize by dragging edge */
\r
2226 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2228 BoardSize newSize = NUM_SIZES - 1;
\r
2229 static int recurse = 0;
\r
2230 if (IsIconic(hwndMain)) return;
\r
2231 if (recurse > 0) return;
\r
2233 while (newSize > 0) {
\r
2234 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2235 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2236 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2239 boardSize = newSize;
\r
2240 InitDrawingSizes(boardSize, flags);
\r
2245 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2248 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2250 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2251 ChessSquare piece;
\r
2252 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2254 SIZE clockSize, messageSize;
\r
2256 char buf[MSG_SIZ];
\r
2258 HMENU hmenu = GetMenu(hwndMain);
\r
2259 RECT crect, wrect, oldRect;
\r
2261 LOGBRUSH logbrush;
\r
2262 VariantClass v = gameInfo.variant;
\r
2264 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2265 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2267 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2268 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2269 if(boardSize == -1) return; // no size defined yet; abort (to allow early call of InitPosition)
\r
2270 oldBoardSize = boardSize;
\r
2272 if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)
\r
2273 { // correct board size to one where built-in pieces exist
\r
2274 if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)
\r
2275 && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range
\r
2276 || (v == VariantShogi && boardSize != SizeModerate) // Japanese-style Shogi
\r
2277 || v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan
\r
2278 || v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy || v == VariantLion ) {
\r
2279 if(boardSize < SizeMediocre) boardSize = SizePetite; else
\r
2280 if(boardSize > SizeModerate) boardSize = SizeBulky; else
\r
2281 boardSize = SizeMiddling;
\r
2284 if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite
\r
2286 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2287 oldRect.top = wpMain.y;
\r
2288 oldRect.right = wpMain.x + wpMain.width;
\r
2289 oldRect.bottom = wpMain.y + wpMain.height;
\r
2291 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2292 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2293 squareSize = sizeInfo[boardSize].squareSize;
\r
2294 lineGap = sizeInfo[boardSize].lineGap;
\r
2295 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2296 border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;
\r
2298 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2299 lineGap = appData.overrideLineGap;
\r
2302 if (tinyLayout != oldTinyLayout) {
\r
2303 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2305 style &= ~WS_SYSMENU;
\r
2306 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2307 "&Minimize\tCtrl+F4");
\r
2309 style |= WS_SYSMENU;
\r
2310 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2312 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2314 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2315 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2316 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2318 DrawMenuBar(hwndMain);
\r
2321 boardWidth = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;
\r
2322 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;
\r
2324 /* Get text area sizes */
\r
2325 hdc = GetDC(hwndMain);
\r
2326 if (appData.clockMode) {
\r
2327 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2329 snprintf(buf, MSG_SIZ, _("White"));
\r
2331 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2332 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2333 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2334 str = _("We only care about the height here");
\r
2335 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2336 SelectObject(hdc, oldFont);
\r
2337 ReleaseDC(hwndMain, hdc);
\r
2339 /* Compute where everything goes */
\r
2340 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2341 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2342 logoHeight = 2*clockSize.cy;
\r
2343 leftLogoRect.left = OUTER_MARGIN;
\r
2344 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2345 leftLogoRect.top = OUTER_MARGIN;
\r
2346 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2348 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2349 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2350 rightLogoRect.top = OUTER_MARGIN;
\r
2351 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2354 whiteRect.left = leftLogoRect.right;
\r
2355 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2356 whiteRect.top = OUTER_MARGIN;
\r
2357 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2359 blackRect.right = rightLogoRect.left;
\r
2360 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2361 blackRect.top = whiteRect.top;
\r
2362 blackRect.bottom = whiteRect.bottom;
\r
2364 whiteRect.left = OUTER_MARGIN;
\r
2365 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2366 whiteRect.top = OUTER_MARGIN;
\r
2367 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2369 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2370 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2371 blackRect.top = whiteRect.top;
\r
2372 blackRect.bottom = whiteRect.bottom;
\r
2374 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2377 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2378 if (appData.showButtonBar) {
\r
2379 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2380 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2382 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2384 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2385 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2387 boardRect.left = OUTER_MARGIN;
\r
2388 boardRect.right = boardRect.left + boardWidth;
\r
2389 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2390 boardRect.bottom = boardRect.top + boardHeight;
\r
2392 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2393 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2394 oldTinyLayout = tinyLayout;
\r
2395 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2396 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2397 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2398 winW *= 1 + twoBoards;
\r
2399 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2400 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2401 wpMain.height = winH; // without disturbing window attachments
\r
2402 GetWindowRect(hwndMain, &wrect);
\r
2403 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2404 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2406 // [HGM] placement: let attached windows follow size change.
\r
2407 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2408 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2409 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2410 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2411 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2413 /* compensate if menu bar wrapped */
\r
2414 GetClientRect(hwndMain, &crect);
\r
2415 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2416 wpMain.height += offby;
\r
2418 case WMSZ_TOPLEFT:
\r
2419 SetWindowPos(hwndMain, NULL,
\r
2420 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2421 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2424 case WMSZ_TOPRIGHT:
\r
2426 SetWindowPos(hwndMain, NULL,
\r
2427 wrect.left, wrect.bottom - wpMain.height,
\r
2428 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2431 case WMSZ_BOTTOMLEFT:
\r
2433 SetWindowPos(hwndMain, NULL,
\r
2434 wrect.right - wpMain.width, wrect.top,
\r
2435 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2438 case WMSZ_BOTTOMRIGHT:
\r
2442 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2443 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2448 for (i = 0; i < N_BUTTONS; i++) {
\r
2449 if (buttonDesc[i].hwnd != NULL) {
\r
2450 DestroyWindow(buttonDesc[i].hwnd);
\r
2451 buttonDesc[i].hwnd = NULL;
\r
2453 if (appData.showButtonBar) {
\r
2454 buttonDesc[i].hwnd =
\r
2455 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2456 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2457 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2458 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2459 (HMENU) buttonDesc[i].id,
\r
2460 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2462 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2463 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2464 MAKELPARAM(FALSE, 0));
\r
2466 if (buttonDesc[i].id == IDM_Pause)
\r
2467 hwndPause = buttonDesc[i].hwnd;
\r
2468 buttonDesc[i].wndproc = (WNDPROC)
\r
2469 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2472 if (gridPen != NULL) DeleteObject(gridPen);
\r
2473 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2474 if (premovePen != NULL) DeleteObject(premovePen);
\r
2475 if (lineGap != 0) {
\r
2476 logbrush.lbStyle = BS_SOLID;
\r
2477 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2479 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2480 lineGap, &logbrush, 0, NULL);
\r
2481 logbrush.lbColor = highlightSquareColor;
\r
2483 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2484 lineGap, &logbrush, 0, NULL);
\r
2486 logbrush.lbColor = premoveHighlightColor;
\r
2488 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2489 lineGap, &logbrush, 0, NULL);
\r
2491 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2492 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2493 gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;
\r
2494 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2495 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;
\r
2496 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2497 BOARD_WIDTH * (squareSize + lineGap) + border;
\r
2498 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2500 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2501 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;
\r
2502 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2503 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2504 lineGap / 2 + (i * (squareSize + lineGap)) + border;
\r
2505 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2506 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;
\r
2507 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2511 /* [HGM] Licensing requirement */
\r
2513 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2516 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2518 GothicPopUp( "", VariantNormal);
\r
2521 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2523 /* Load piece bitmaps for this board size */
\r
2524 for (i=0; i<=2; i++) {
\r
2525 for (piece = WhitePawn;
\r
2526 (int) piece < (int) BlackPawn;
\r
2527 piece = (ChessSquare) ((int) piece + 1)) {
\r
2528 if (pieceBitmap[i][piece] != NULL)
\r
2529 DeleteObject(pieceBitmap[i][piece]);
\r
2533 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2534 // Orthodox Chess pieces
\r
2535 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2536 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2537 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2538 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2539 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2540 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2541 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2542 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2543 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2544 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2545 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2546 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2547 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2548 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2549 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2550 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2551 // in Shogi, Hijack the unused Queen for Lance
\r
2552 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2553 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2554 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2556 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2557 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2558 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2561 if(squareSize <= 72 && squareSize >= 33) {
\r
2562 /* A & C are available in most sizes now */
\r
2563 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2564 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2565 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2566 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2567 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2568 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2569 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2570 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2571 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2572 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2573 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2574 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2575 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2576 } else { // Smirf-like
\r
2577 if(gameInfo.variant == VariantSChess) {
\r
2578 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2579 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2580 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2582 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2583 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2584 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2587 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2588 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2589 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2590 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2591 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2592 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2593 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2594 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2595 } else { // WinBoard standard
\r
2596 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2597 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2598 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2603 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2604 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2605 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2606 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2607 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2608 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2609 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2610 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2611 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2612 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2613 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2614 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2615 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2616 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2617 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2618 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2619 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2620 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2621 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2622 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2623 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2624 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2625 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2626 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2627 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2628 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2629 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2630 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2631 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2632 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2633 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2634 pieceBitmap[0][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "s");
\r
2635 pieceBitmap[1][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "o");
\r
2636 pieceBitmap[2][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "w");
\r
2638 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2639 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2640 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2641 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2642 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2643 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2644 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2645 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2646 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2647 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2648 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2649 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2650 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2652 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2653 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2654 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2655 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2656 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2657 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2658 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2659 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2660 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2661 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2662 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2663 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2666 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2667 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2668 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2669 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2670 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2671 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2672 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2673 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2674 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2675 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2676 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2677 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2678 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2679 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2680 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2684 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2685 /* special Shogi support in this size */
\r
2686 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2687 for (piece = WhitePawn;
\r
2688 (int) piece < (int) BlackPawn;
\r
2689 piece = (ChessSquare) ((int) piece + 1)) {
\r
2690 if (pieceBitmap[i][piece] != NULL)
\r
2691 DeleteObject(pieceBitmap[i][piece]);
\r
2694 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2695 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2696 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2697 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2698 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2699 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2700 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2701 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2702 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2703 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2704 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2705 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2706 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2707 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2708 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2709 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2710 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2711 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2712 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2713 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2714 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2715 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2716 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2717 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2718 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2719 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2720 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2721 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2722 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2723 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2724 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2725 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2726 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2727 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2728 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2729 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2730 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2731 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2732 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2733 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2734 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2735 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2741 PieceBitmap(ChessSquare p, int kind)
\r
2743 if ((int) p >= (int) BlackPawn)
\r
2744 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2746 return pieceBitmap[kind][(int) p];
\r
2749 /***************************************************************/
\r
2751 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2752 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2754 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2755 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2759 SquareToPos(int row, int column, int * x, int * y)
\r
2762 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
2763 *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;
\r
2765 *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;
\r
2766 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
2771 DrawCoordsOnDC(HDC hdc)
\r
2773 static char files[] = "0123456789012345678901221098765432109876543210";
\r
2774 static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";
\r
2775 char str[2] = { NULLCHAR, NULLCHAR };
\r
2776 int oldMode, oldAlign, x, y, start, i;
\r
2780 if (!appData.showCoords)
\r
2783 start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;
\r
2785 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2786 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2787 oldAlign = GetTextAlign(hdc);
\r
2788 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2790 y = boardRect.top + lineGap;
\r
2791 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2794 SetTextAlign(hdc, TA_RIGHT|TA_TOP);
\r
2795 x += border - lineGap - 4; y += squareSize - 6;
\r
2797 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2798 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2799 str[0] = files[start + i];
\r
2800 ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);
\r
2801 y += squareSize + lineGap;
\r
2804 start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;
\r
2807 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2808 x += -border + 4; y += border - squareSize + 6;
\r
2810 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2811 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2812 str[0] = ranks[start + i];
\r
2813 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2814 x += squareSize + lineGap;
\r
2817 SelectObject(hdc, oldBrush);
\r
2818 SetBkMode(hdc, oldMode);
\r
2819 SetTextAlign(hdc, oldAlign);
\r
2820 SelectObject(hdc, oldFont);
\r
2824 DrawGridOnDC(HDC hdc)
\r
2828 if (lineGap != 0) {
\r
2829 oldPen = SelectObject(hdc, gridPen);
\r
2830 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2831 SelectObject(hdc, oldPen);
\r
2835 #define HIGHLIGHT_PEN 0
\r
2836 #define PREMOVE_PEN 1
\r
2839 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2842 HPEN oldPen, hPen;
\r
2843 if (lineGap == 0) return;
\r
2845 x1 = boardRect.left +
\r
2846 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;
\r
2847 y1 = boardRect.top +
\r
2848 lineGap/2 + y * (squareSize + lineGap) + border;
\r
2850 x1 = boardRect.left +
\r
2851 lineGap/2 + x * (squareSize + lineGap) + border;
\r
2852 y1 = boardRect.top +
\r
2853 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;
\r
2855 hPen = pen ? premovePen : highlightPen;
\r
2856 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2857 MoveToEx(hdc, x1, y1, NULL);
\r
2858 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2859 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2860 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2861 LineTo(hdc, x1, y1);
\r
2862 SelectObject(hdc, oldPen);
\r
2866 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2869 for (i=0; i<2; i++) {
\r
2870 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2871 DrawHighlightOnDC(hdc, TRUE,
\r
2872 h->sq[i].x, h->sq[i].y,
\r
2877 /* Note: sqcolor is used only in monoMode */
\r
2878 /* Note that this code is largely duplicated in woptions.c,
\r
2879 function DrawSampleSquare, so that needs to be updated too */
\r
2881 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2883 HBITMAP oldBitmap;
\r
2887 if (appData.blindfold) return;
\r
2889 /* [AS] Use font-based pieces if needed */
\r
2890 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2891 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2892 CreatePiecesFromFont();
\r
2894 if( fontBitmapSquareSize == squareSize ) {
\r
2895 int index = TranslatePieceToFontPiece(piece);
\r
2897 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2899 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2900 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2904 squareSize, squareSize,
\r
2909 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2911 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2912 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2916 squareSize, squareSize,
\r
2925 if (appData.monoMode) {
\r
2926 SelectObject(tmphdc, PieceBitmap(piece,
\r
2927 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2928 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2929 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2931 HBRUSH xBrush = whitePieceBrush;
\r
2932 tmpSize = squareSize;
\r
2933 if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);
\r
2935 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2936 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2937 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2938 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2939 x += (squareSize - minorSize)>>1;
\r
2940 y += squareSize - minorSize - 2;
\r
2941 tmpSize = minorSize;
\r
2943 if (color || appData.allWhite ) {
\r
2944 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2946 oldBrush = SelectObject(hdc, xBrush);
\r
2947 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2948 if(appData.upsideDown && color==flipView)
\r
2949 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2951 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2952 /* Use black for outline of white pieces */
\r
2953 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2954 if(appData.upsideDown && color==flipView)
\r
2955 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2957 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2958 } else if(appData.pieceDirectory[0]) {
\r
2959 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2960 oldBrush = SelectObject(hdc, xBrush);
\r
2961 if(appData.upsideDown && color==flipView)
\r
2962 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2964 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2965 SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2966 if(appData.upsideDown && color==flipView)
\r
2967 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2969 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2971 /* Use square color for details of black pieces */
\r
2972 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2973 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2974 if(appData.upsideDown && !flipView)
\r
2975 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2977 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2979 SelectObject(hdc, oldBrush);
\r
2980 SelectObject(tmphdc, oldBitmap);
\r
2984 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2985 int GetBackTextureMode( int algo )
\r
2987 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2991 case BACK_TEXTURE_MODE_PLAIN:
\r
2992 result = 1; /* Always use identity map */
\r
2994 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2995 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
3003 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
3004 to handle redraws cleanly (as random numbers would always be different).
\r
3006 VOID RebuildTextureSquareInfo()
\r
3016 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
3018 if( liteBackTexture != NULL ) {
\r
3019 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3020 lite_w = bi.bmWidth;
\r
3021 lite_h = bi.bmHeight;
\r
3025 if( darkBackTexture != NULL ) {
\r
3026 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3027 dark_w = bi.bmWidth;
\r
3028 dark_h = bi.bmHeight;
\r
3032 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
3033 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
3034 if( (col + row) & 1 ) {
\r
3036 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
3037 if( lite_w >= squareSize*BOARD_WIDTH )
\r
3038 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
3040 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
3041 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
3042 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
3044 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
3045 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
3050 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
3051 if( dark_w >= squareSize*BOARD_WIDTH )
\r
3052 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
3054 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
3055 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
3056 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
3058 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
3059 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
3066 /* [AS] Arrow highlighting support */
\r
3068 static double A_WIDTH = 5; /* Width of arrow body */
\r
3070 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
3071 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
3073 static double Sqr( double x )
\r
3078 static int Round( double x )
\r
3080 return (int) (x + 0.5);
\r
3083 /* Draw an arrow between two points using current settings */
\r
3084 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
3087 double dx, dy, j, k, x, y;
\r
3089 if( d_x == s_x ) {
\r
3090 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3092 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
3095 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
3096 arrow[1].y = d_y - h;
\r
3098 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3099 arrow[2].y = d_y - h;
\r
3104 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3105 arrow[5].y = d_y - h;
\r
3107 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3108 arrow[4].y = d_y - h;
\r
3110 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3113 else if( d_y == s_y ) {
\r
3114 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3117 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3119 arrow[1].x = d_x - w;
\r
3120 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3122 arrow[2].x = d_x - w;
\r
3123 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3128 arrow[5].x = d_x - w;
\r
3129 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3131 arrow[4].x = d_x - w;
\r
3132 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3135 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3138 /* [AS] Needed a lot of paper for this! :-) */
\r
3139 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3140 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3142 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3144 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3149 arrow[0].x = Round(x - j);
\r
3150 arrow[0].y = Round(y + j*dx);
\r
3152 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3153 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3156 x = (double) d_x - k;
\r
3157 y = (double) d_y - k*dy;
\r
3160 x = (double) d_x + k;
\r
3161 y = (double) d_y + k*dy;
\r
3164 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3166 arrow[6].x = Round(x - j);
\r
3167 arrow[6].y = Round(y + j*dx);
\r
3169 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3170 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3172 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3173 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3178 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3179 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3182 Polygon( hdc, arrow, 7 );
\r
3185 /* [AS] Draw an arrow between two squares */
\r
3186 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3188 int s_x, s_y, d_x, d_y;
\r
3195 if( s_col == d_col && s_row == d_row ) {
\r
3199 /* Get source and destination points */
\r
3200 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3201 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3204 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3206 else if( d_y < s_y ) {
\r
3207 d_y += squareSize / 2 + squareSize / 4;
\r
3210 d_y += squareSize / 2;
\r
3214 d_x += squareSize / 2 - squareSize / 4;
\r
3216 else if( d_x < s_x ) {
\r
3217 d_x += squareSize / 2 + squareSize / 4;
\r
3220 d_x += squareSize / 2;
\r
3223 s_x += squareSize / 2;
\r
3224 s_y += squareSize / 2;
\r
3226 /* Adjust width */
\r
3227 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3230 stLB.lbStyle = BS_SOLID;
\r
3231 stLB.lbColor = appData.highlightArrowColor;
\r
3234 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3235 holdpen = SelectObject( hdc, hpen );
\r
3236 hbrush = CreateBrushIndirect( &stLB );
\r
3237 holdbrush = SelectObject( hdc, hbrush );
\r
3239 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3241 SelectObject( hdc, holdpen );
\r
3242 SelectObject( hdc, holdbrush );
\r
3243 DeleteObject( hpen );
\r
3244 DeleteObject( hbrush );
\r
3247 BOOL HasHighlightInfo()
\r
3249 BOOL result = FALSE;
\r
3251 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3252 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3263 BOOL IsDrawArrowEnabled()
\r
3265 BOOL result = FALSE;
\r
3267 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3274 VOID DrawArrowHighlight( HDC hdc )
\r
3276 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3277 DrawArrowBetweenSquares( hdc,
\r
3278 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3279 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3283 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3285 HRGN result = NULL;
\r
3287 if( HasHighlightInfo() ) {
\r
3288 int x1, y1, x2, y2;
\r
3289 int sx, sy, dx, dy;
\r
3291 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3292 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3294 sx = MIN( x1, x2 );
\r
3295 sy = MIN( y1, y2 );
\r
3296 dx = MAX( x1, x2 ) + squareSize;
\r
3297 dy = MAX( y1, y2 ) + squareSize;
\r
3299 result = CreateRectRgn( sx, sy, dx, dy );
\r
3306 Warning: this function modifies the behavior of several other functions.
\r
3308 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3309 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3310 repaint is scattered all over the place, which is not good for features such as
\r
3311 "arrow highlighting" that require a full repaint of the board.
\r
3313 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3314 user interaction, when speed is not so important) but especially to avoid errors
\r
3315 in the displayed graphics.
\r
3317 In such patched places, I always try refer to this function so there is a single
\r
3318 place to maintain knowledge.
\r
3320 To restore the original behavior, just return FALSE unconditionally.
\r
3322 BOOL IsFullRepaintPreferrable()
\r
3324 BOOL result = FALSE;
\r
3326 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3327 /* Arrow may appear on the board */
\r
3335 This function is called by DrawPosition to know whether a full repaint must
\r
3338 Only DrawPosition may directly call this function, which makes use of
\r
3339 some state information. Other function should call DrawPosition specifying
\r
3340 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3342 BOOL DrawPositionNeedsFullRepaint()
\r
3344 BOOL result = FALSE;
\r
3347 Probably a slightly better policy would be to trigger a full repaint
\r
3348 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3349 but animation is fast enough that it's difficult to notice.
\r
3351 if( animInfo.piece == EmptySquare ) {
\r
3352 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3360 static HBITMAP borderBitmap;
\r
3363 DrawBackgroundOnDC(HDC hdc)
\r
3369 static char oldBorder[MSG_SIZ];
\r
3370 int w = 600, h = 600, mode;
\r
3372 if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid
\r
3373 strncpy(oldBorder, appData.border, MSG_SIZ-1);
\r
3374 borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
3376 if(borderBitmap == NULL) { // loading failed, use white
\r
3377 FillRect( hdc, &boardRect, whitePieceBrush );
\r
3380 tmphdc = CreateCompatibleDC(hdc);
\r
3381 hbm = SelectObject(tmphdc, borderBitmap);
\r
3382 if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {
\r
3386 mode = SetStretchBltMode(hdc, COLORONCOLOR);
\r
3387 StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left,
\r
3388 boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3389 SetStretchBltMode(hdc, mode);
\r
3390 SelectObject(tmphdc, hbm);
\r
3395 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3397 int row, column, x, y, square_color, piece_color;
\r
3398 ChessSquare piece;
\r
3400 HDC texture_hdc = NULL;
\r
3402 /* [AS] Initialize background textures if needed */
\r
3403 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3404 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3405 if( backTextureSquareSize != squareSize
\r
3406 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3407 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3408 backTextureSquareSize = squareSize;
\r
3409 RebuildTextureSquareInfo();
\r
3412 texture_hdc = CreateCompatibleDC( hdc );
\r
3415 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3416 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3418 SquareToPos(row, column, &x, &y);
\r
3420 piece = board[row][column];
\r
3422 square_color = ((column + row) % 2) == 1;
\r
3423 if( gameInfo.variant == VariantXiangqi ) {
\r
3424 square_color = !InPalace(row, column);
\r
3425 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3426 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3428 piece_color = (int) piece < (int) BlackPawn;
\r
3431 /* [HGM] holdings file: light square or black */
\r
3432 if(column == BOARD_LEFT-2) {
\r
3433 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3436 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3440 if(column == BOARD_RGHT + 1 ) {
\r
3441 if( row < gameInfo.holdingsSize )
\r
3444 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3448 if(column == BOARD_LEFT-1 ) /* left align */
\r
3449 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3450 else if( column == BOARD_RGHT) /* right align */
\r
3451 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3452 else if( piece == DarkSquare) DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3454 if (appData.monoMode) {
\r
3455 if (piece == EmptySquare) {
\r
3456 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3457 square_color ? WHITENESS : BLACKNESS);
\r
3459 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3462 else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {
\r
3463 /* [AS] Draw the square using a texture bitmap */
\r
3464 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3465 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3466 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3469 squareSize, squareSize,
\r
3472 backTextureSquareInfo[r][c].mode,
\r
3473 backTextureSquareInfo[r][c].x,
\r
3474 backTextureSquareInfo[r][c].y );
\r
3476 SelectObject( texture_hdc, hbm );
\r
3478 if (piece != EmptySquare) {
\r
3479 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3483 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3485 oldBrush = SelectObject(hdc, brush );
\r
3486 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3487 SelectObject(hdc, oldBrush);
\r
3488 if (piece != EmptySquare)
\r
3489 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3494 if( texture_hdc != NULL ) {
\r
3495 DeleteDC( texture_hdc );
\r
3499 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3500 void fputDW(FILE *f, int x)
\r
3502 fputc(x & 255, f);
\r
3503 fputc(x>>8 & 255, f);
\r
3504 fputc(x>>16 & 255, f);
\r
3505 fputc(x>>24 & 255, f);
\r
3508 #define MAX_CLIPS 200 /* more than enough */
\r
3511 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3513 // HBITMAP bufferBitmap;
\r
3518 int w = 100, h = 50;
\r
3520 if(logo == NULL) {
\r
3521 if(!logoHeight) return;
\r
3522 FillRect( hdc, &logoRect, whitePieceBrush );
\r
3524 // GetClientRect(hwndMain, &Rect);
\r
3525 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3526 // Rect.bottom-Rect.top+1);
\r
3527 tmphdc = CreateCompatibleDC(hdc);
\r
3528 hbm = SelectObject(tmphdc, logo);
\r
3529 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3533 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3534 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3535 SelectObject(tmphdc, hbm);
\r
3543 HDC hdc = GetDC(hwndMain);
\r
3544 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3545 if(appData.autoLogo) {
\r
3547 switch(gameMode) { // pick logos based on game mode
\r
3548 case IcsObserving:
\r
3549 whiteLogo = second.programLogo; // ICS logo
\r
3550 blackLogo = second.programLogo;
\r
3553 case IcsPlayingWhite:
\r
3554 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3555 blackLogo = second.programLogo; // ICS logo
\r
3557 case IcsPlayingBlack:
\r
3558 whiteLogo = second.programLogo; // ICS logo
\r
3559 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3561 case TwoMachinesPlay:
\r
3562 if(first.twoMachinesColor[0] == 'b') {
\r
3563 whiteLogo = second.programLogo;
\r
3564 blackLogo = first.programLogo;
\r
3567 case MachinePlaysWhite:
\r
3568 blackLogo = userLogo;
\r
3570 case MachinePlaysBlack:
\r
3571 whiteLogo = userLogo;
\r
3572 blackLogo = first.programLogo;
\r
3575 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3576 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3577 ReleaseDC(hwndMain, hdc);
\r
3582 UpdateLogos(int display)
\r
3583 { // called after loading new engine(s), in tourney or from menu
\r
3584 LoadLogo(&first, 0, FALSE);
\r
3585 LoadLogo(&second, 1, appData.icsActive);
\r
3586 InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos
\r
3587 if(display) DisplayLogos();
\r
3590 static HDC hdcSeek;
\r
3592 // [HGM] seekgraph
\r
3593 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3596 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3597 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3598 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3599 SelectObject( hdcSeek, hp );
\r
3602 // front-end wrapper for drawing functions to do rectangles
\r
3603 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3608 if (hdcSeek == NULL) {
\r
3609 hdcSeek = GetDC(hwndMain);
\r
3610 if (!appData.monoMode) {
\r
3611 SelectPalette(hdcSeek, hPal, FALSE);
\r
3612 RealizePalette(hdcSeek);
\r
3615 hp = SelectObject( hdcSeek, gridPen );
\r
3616 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3617 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3618 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3619 SelectObject( hdcSeek, hp );
\r
3622 // front-end wrapper for putting text in graph
\r
3623 void DrawSeekText(char *buf, int x, int y)
\r
3626 SetBkMode( hdcSeek, TRANSPARENT );
\r
3627 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3628 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3631 void DrawSeekDot(int x, int y, int color)
\r
3633 int square = color & 0x80;
\r
3634 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3635 color == 0 ? markerBrush[1] : color == 1 ? darkSquareBrush : explodeBrush);
\r
3638 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3639 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3641 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3642 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3643 SelectObject(hdcSeek, oldBrush);
\r
3646 void DrawSeekOpen()
\r
3650 void DrawSeekClose()
\r
3655 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3657 static Board lastReq[2], lastDrawn[2];
\r
3658 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3659 static int lastDrawnFlipView = 0;
\r
3660 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3661 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3664 HBITMAP bufferBitmap;
\r
3665 HBITMAP oldBitmap;
\r
3667 HRGN clips[MAX_CLIPS];
\r
3668 ChessSquare dragged_piece = EmptySquare;
\r
3669 int nr = twoBoards*partnerUp;
\r
3671 /* I'm undecided on this - this function figures out whether a full
\r
3672 * repaint is necessary on its own, so there's no real reason to have the
\r
3673 * caller tell it that. I think this can safely be set to FALSE - but
\r
3674 * if we trust the callers not to request full repaints unnessesarily, then
\r
3675 * we could skip some clipping work. In other words, only request a full
\r
3676 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3677 * gamestart and similar) --Hawk
\r
3679 Boolean fullrepaint = repaint;
\r
3681 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3683 if( DrawPositionNeedsFullRepaint() ) {
\r
3684 fullrepaint = TRUE;
\r
3687 if (board == NULL) {
\r
3688 if (!lastReqValid[nr]) {
\r
3691 board = lastReq[nr];
\r
3693 CopyBoard(lastReq[nr], board);
\r
3694 lastReqValid[nr] = 1;
\r
3697 if (doingSizing) {
\r
3701 if (IsIconic(hwndMain)) {
\r
3705 if (hdc == NULL) {
\r
3706 hdc = GetDC(hwndMain);
\r
3707 if (!appData.monoMode) {
\r
3708 SelectPalette(hdc, hPal, FALSE);
\r
3709 RealizePalette(hdc);
\r
3713 releaseDC = FALSE;
\r
3716 /* Create some work-DCs */
\r
3717 hdcmem = CreateCompatibleDC(hdc);
\r
3718 tmphdc = CreateCompatibleDC(hdc);
\r
3720 /* If dragging is in progress, we temporarely remove the piece */
\r
3721 /* [HGM] or temporarily decrease count if stacked */
\r
3722 /* !! Moved to before board compare !! */
\r
3723 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3724 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3725 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3726 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3727 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3729 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3730 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3731 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3733 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3736 /* Figure out which squares need updating by comparing the
\r
3737 * newest board with the last drawn board and checking if
\r
3738 * flipping has changed.
\r
3740 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3741 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3742 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3743 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3744 SquareToPos(row, column, &x, &y);
\r
3745 clips[num_clips++] =
\r
3746 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3750 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3751 for (i=0; i<2; i++) {
\r
3752 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3753 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3754 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3755 lastDrawnHighlight.sq[i].y >= 0) {
\r
3756 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3757 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3758 clips[num_clips++] =
\r
3759 CreateRectRgn(x - lineGap, y - lineGap,
\r
3760 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3762 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3763 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3764 clips[num_clips++] =
\r
3765 CreateRectRgn(x - lineGap, y - lineGap,
\r
3766 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3770 for (i=0; i<2; i++) {
\r
3771 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3772 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3773 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3774 lastDrawnPremove.sq[i].y >= 0) {
\r
3775 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3776 lastDrawnPremove.sq[i].x, &x, &y);
\r
3777 clips[num_clips++] =
\r
3778 CreateRectRgn(x - lineGap, y - lineGap,
\r
3779 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3781 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3782 premoveHighlightInfo.sq[i].y >= 0) {
\r
3783 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3784 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3785 clips[num_clips++] =
\r
3786 CreateRectRgn(x - lineGap, y - lineGap,
\r
3787 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3791 } else { // nr == 1
\r
3792 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3793 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3794 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3795 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3796 for (i=0; i<2; i++) {
\r
3797 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3798 partnerHighlightInfo.sq[i].y >= 0) {
\r
3799 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3800 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3801 clips[num_clips++] =
\r
3802 CreateRectRgn(x - lineGap, y - lineGap,
\r
3803 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3805 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3806 oldPartnerHighlight.sq[i].y >= 0) {
\r
3807 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3808 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3809 clips[num_clips++] =
\r
3810 CreateRectRgn(x - lineGap, y - lineGap,
\r
3811 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3816 fullrepaint = TRUE;
\r
3819 /* Create a buffer bitmap - this is the actual bitmap
\r
3820 * being written to. When all the work is done, we can
\r
3821 * copy it to the real DC (the screen). This avoids
\r
3822 * the problems with flickering.
\r
3824 GetClientRect(hwndMain, &Rect);
\r
3825 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3826 Rect.bottom-Rect.top+1);
\r
3827 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3828 if (!appData.monoMode) {
\r
3829 SelectPalette(hdcmem, hPal, FALSE);
\r
3832 /* Create clips for dragging */
\r
3833 if (!fullrepaint) {
\r
3834 if (dragInfo.from.x >= 0) {
\r
3835 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3836 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3838 if (dragInfo.start.x >= 0) {
\r
3839 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3840 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3842 if (dragInfo.pos.x >= 0) {
\r
3843 x = dragInfo.pos.x - squareSize / 2;
\r
3844 y = dragInfo.pos.y - squareSize / 2;
\r
3845 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3847 if (dragInfo.lastpos.x >= 0) {
\r
3848 x = dragInfo.lastpos.x - squareSize / 2;
\r
3849 y = dragInfo.lastpos.y - squareSize / 2;
\r
3850 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3854 /* Are we animating a move?
\r
3856 * - remove the piece from the board (temporarely)
\r
3857 * - calculate the clipping region
\r
3859 if (!fullrepaint) {
\r
3860 if (animInfo.piece != EmptySquare) {
\r
3861 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3862 x = boardRect.left + animInfo.lastpos.x;
\r
3863 y = boardRect.top + animInfo.lastpos.y;
\r
3864 x2 = boardRect.left + animInfo.pos.x;
\r
3865 y2 = boardRect.top + animInfo.pos.y;
\r
3866 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3867 /* Slight kludge. The real problem is that after AnimateMove is
\r
3868 done, the position on the screen does not match lastDrawn.
\r
3869 This currently causes trouble only on e.p. captures in
\r
3870 atomic, where the piece moves to an empty square and then
\r
3871 explodes. The old and new positions both had an empty square
\r
3872 at the destination, but animation has drawn a piece there and
\r
3873 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3874 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3878 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3879 if (num_clips == 0)
\r
3880 fullrepaint = TRUE;
\r
3882 /* Set clipping on the memory DC */
\r
3883 if (!fullrepaint) {
\r
3884 SelectClipRgn(hdcmem, clips[0]);
\r
3885 for (x = 1; x < num_clips; x++) {
\r
3886 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3887 abort(); // this should never ever happen!
\r
3891 /* Do all the drawing to the memory DC */
\r
3892 if(explodeInfo.radius) { // [HGM] atomic
\r
3894 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3895 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3896 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3897 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3898 x += squareSize/2;
\r
3899 y += squareSize/2;
\r
3900 if(!fullrepaint) {
\r
3901 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3902 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3904 DrawGridOnDC(hdcmem);
\r
3905 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3906 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3907 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3908 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3909 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3910 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3911 SelectObject(hdcmem, oldBrush);
\r
3913 if(border) DrawBackgroundOnDC(hdcmem);
\r
3914 DrawGridOnDC(hdcmem);
\r
3915 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3916 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3917 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3919 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3920 oldPartnerHighlight = partnerHighlightInfo;
\r
3922 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3924 if(nr == 0) // [HGM] dual: markers only on left board
\r
3925 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3926 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3927 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3928 HBRUSH oldBrush = SelectObject(hdcmem, markerBrush[marker[row][column]-1]);
\r
3929 SquareToPos(row, column, &x, &y);
\r
3930 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3931 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3932 SelectObject(hdcmem, oldBrush);
\r
3937 if( appData.highlightMoveWithArrow ) {
\r
3938 DrawArrowHighlight(hdcmem);
\r
3941 DrawCoordsOnDC(hdcmem);
\r
3943 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3944 /* to make sure lastDrawn contains what is actually drawn */
\r
3946 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3947 if (dragged_piece != EmptySquare) {
\r
3948 /* [HGM] or restack */
\r
3949 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3950 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3952 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3953 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3954 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3955 x = dragInfo.pos.x - squareSize / 2;
\r
3956 y = dragInfo.pos.y - squareSize / 2;
\r
3957 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
3958 ((int) dragInfo.piece < (int) BlackPawn),
\r
3959 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3962 /* Put the animated piece back into place and draw it */
\r
3963 if (animInfo.piece != EmptySquare) {
\r
3964 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3965 x = boardRect.left + animInfo.pos.x;
\r
3966 y = boardRect.top + animInfo.pos.y;
\r
3967 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3968 ((int) animInfo.piece < (int) BlackPawn),
\r
3969 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3972 /* Release the bufferBitmap by selecting in the old bitmap
\r
3973 * and delete the memory DC
\r
3975 SelectObject(hdcmem, oldBitmap);
\r
3978 /* Set clipping on the target DC */
\r
3979 if (!fullrepaint) {
\r
3980 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3982 GetRgnBox(clips[x], &rect);
\r
3983 DeleteObject(clips[x]);
\r
3984 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3985 rect.right + wpMain.width/2, rect.bottom);
\r
3987 SelectClipRgn(hdc, clips[0]);
\r
3988 for (x = 1; x < num_clips; x++) {
\r
3989 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3990 abort(); // this should never ever happen!
\r
3994 /* Copy the new bitmap onto the screen in one go.
\r
3995 * This way we avoid any flickering
\r
3997 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3998 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3999 boardRect.right - boardRect.left,
\r
4000 boardRect.bottom - boardRect.top,
\r
4001 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
4002 if(saveDiagFlag) {
\r
4003 BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData;
\r
4004 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
4006 GetObject(bufferBitmap, sizeof(b), &b);
\r
4007 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\r
4008 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
4009 bih.biWidth = b.bmWidth;
\r
4010 bih.biHeight = b.bmHeight;
\r
4012 bih.biBitCount = b.bmBitsPixel;
\r
4013 bih.biCompression = 0;
\r
4014 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
4015 bih.biXPelsPerMeter = 0;
\r
4016 bih.biYPelsPerMeter = 0;
\r
4017 bih.biClrUsed = 0;
\r
4018 bih.biClrImportant = 0;
\r
4019 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
4020 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
4021 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
4022 // fprintf(diagFile, "%8x\n", (int) pData);
\r
4024 wb = b.bmWidthBytes;
\r
4026 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
4027 int k = ((int*) pData)[i];
\r
4028 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4029 if(j >= 16) break;
\r
4031 if(j >= nrColors) nrColors = j+1;
\r
4033 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
4035 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
4036 for(w=0; w<(wb>>2); w+=2) {
\r
4037 int k = ((int*) pData)[(wb*i>>2) + w];
\r
4038 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4039 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
4040 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
4041 pData[p++] = m | j<<4;
\r
4043 while(p&3) pData[p++] = 0;
\r
4046 wb = ((wb+31)>>5)<<2;
\r
4048 // write BITMAPFILEHEADER
\r
4049 fprintf(diagFile, "BM");
\r
4050 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
4051 fputDW(diagFile, 0);
\r
4052 fputDW(diagFile, 0x36 + (fac?64:0));
\r
4053 // write BITMAPINFOHEADER
\r
4054 fputDW(diagFile, 40);
\r
4055 fputDW(diagFile, b.bmWidth);
\r
4056 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
4057 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
4058 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
4059 fputDW(diagFile, 0);
\r
4060 fputDW(diagFile, 0);
\r
4061 fputDW(diagFile, 0);
\r
4062 fputDW(diagFile, 0);
\r
4063 fputDW(diagFile, 0);
\r
4064 fputDW(diagFile, 0);
\r
4065 // write color table
\r
4067 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
4068 // write bitmap data
\r
4069 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
4070 fputc(pData[i], diagFile);
\r
4075 SelectObject(tmphdc, oldBitmap);
\r
4077 /* Massive cleanup */
\r
4078 for (x = 0; x < num_clips; x++)
\r
4079 DeleteObject(clips[x]);
\r
4082 DeleteObject(bufferBitmap);
\r
4085 ReleaseDC(hwndMain, hdc);
\r
4087 if (lastDrawnFlipView != flipView && nr == 0) {
\r
4089 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
4091 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
4094 /* CopyBoard(lastDrawn, board);*/
\r
4095 lastDrawnHighlight = highlightInfo;
\r
4096 lastDrawnPremove = premoveHighlightInfo;
\r
4097 lastDrawnFlipView = flipView;
\r
4098 lastDrawnValid[nr] = 1;
\r
4101 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
4106 saveDiagFlag = 1; diagFile = f;
\r
4107 HDCDrawPosition(NULL, TRUE, NULL);
\r
4115 /*---------------------------------------------------------------------------*\
\r
4116 | CLIENT PAINT PROCEDURE
\r
4117 | This is the main event-handler for the WM_PAINT message.
\r
4119 \*---------------------------------------------------------------------------*/
\r
4121 PaintProc(HWND hwnd)
\r
4127 if((hdc = BeginPaint(hwnd, &ps))) {
\r
4128 if (IsIconic(hwnd)) {
\r
4129 DrawIcon(hdc, 2, 2, iconCurrent);
\r
4131 if (!appData.monoMode) {
\r
4132 SelectPalette(hdc, hPal, FALSE);
\r
4133 RealizePalette(hdc);
\r
4135 HDCDrawPosition(hdc, 1, NULL);
\r
4136 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
4137 flipView = !flipView; partnerUp = !partnerUp;
\r
4138 HDCDrawPosition(hdc, 1, NULL);
\r
4139 flipView = !flipView; partnerUp = !partnerUp;
\r
4142 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
4143 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
4144 ETO_CLIPPED|ETO_OPAQUE,
\r
4145 &messageRect, messageText, strlen(messageText), NULL);
\r
4146 SelectObject(hdc, oldFont);
\r
4147 DisplayBothClocks();
\r
4150 EndPaint(hwnd,&ps);
\r
4158 * If the user selects on a border boundary, return -1; if off the board,
\r
4159 * return -2. Otherwise map the event coordinate to the square.
\r
4160 * The offset boardRect.left or boardRect.top must already have been
\r
4161 * subtracted from x.
\r
4163 int EventToSquare(x, limit)
\r
4168 if (x < lineGap + border)
\r
4170 x -= lineGap + border;
\r
4171 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4173 x /= (squareSize + lineGap);
\r
4185 DropEnable dropEnables[] = {
\r
4186 { 'P', DP_Pawn, N_("Pawn") },
\r
4187 { 'N', DP_Knight, N_("Knight") },
\r
4188 { 'B', DP_Bishop, N_("Bishop") },
\r
4189 { 'R', DP_Rook, N_("Rook") },
\r
4190 { 'Q', DP_Queen, N_("Queen") },
\r
4194 SetupDropMenu(HMENU hmenu)
\r
4196 int i, count, enable;
\r
4198 extern char white_holding[], black_holding[];
\r
4199 char item[MSG_SIZ];
\r
4201 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4202 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4203 dropEnables[i].piece);
\r
4205 while (p && *p++ == dropEnables[i].piece) count++;
\r
4206 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4207 enable = count > 0 || !appData.testLegality
\r
4208 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4209 && !appData.icsActive);
\r
4210 ModifyMenu(hmenu, dropEnables[i].command,
\r
4211 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4212 dropEnables[i].command, item);
\r
4216 void DragPieceBegin(int x, int y, Boolean instantly)
\r
4218 dragInfo.lastpos.x = boardRect.left + x;
\r
4219 dragInfo.lastpos.y = boardRect.top + y;
\r
4220 if(instantly) dragInfo.pos = dragInfo.lastpos;
\r
4221 dragInfo.from.x = fromX;
\r
4222 dragInfo.from.y = fromY;
\r
4223 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4224 dragInfo.start = dragInfo.from;
\r
4225 SetCapture(hwndMain);
\r
4228 void DragPieceEnd(int x, int y)
\r
4231 dragInfo.start.x = dragInfo.start.y = -1;
\r
4232 dragInfo.from = dragInfo.start;
\r
4233 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4236 void ChangeDragPiece(ChessSquare piece)
\r
4238 dragInfo.piece = piece;
\r
4241 /* Event handler for mouse messages */
\r
4243 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4247 static int recursive = 0;
\r
4249 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4252 if (message == WM_MBUTTONUP) {
\r
4253 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4254 to the middle button: we simulate pressing the left button too!
\r
4256 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4257 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4263 pt.x = LOWORD(lParam);
\r
4264 pt.y = HIWORD(lParam);
\r
4265 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4266 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4267 if (!flipView && y >= 0) {
\r
4268 y = BOARD_HEIGHT - 1 - y;
\r
4270 if (flipView && x >= 0) {
\r
4271 x = BOARD_WIDTH - 1 - x;
\r
4274 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4275 controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status
\r
4277 switch (message) {
\r
4278 case WM_LBUTTONDOWN:
\r
4279 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4280 ClockClick(flipClock); break;
\r
4281 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4282 ClockClick(!flipClock); break;
\r
4284 if(dragging) { // [HGM] lion: don't destroy dragging info if we are already dragging
\r
4285 dragInfo.start.x = dragInfo.start.y = -1;
\r
4286 dragInfo.from = dragInfo.start;
\r
4288 if(fromX == -1 && frozen) { // not sure where this is for
\r
4289 fromX = fromY = -1;
\r
4290 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4293 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4294 DrawPosition(TRUE, NULL);
\r
4297 case WM_LBUTTONUP:
\r
4298 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4299 DrawPosition(TRUE, NULL);
\r
4302 case WM_MOUSEMOVE:
\r
4303 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4304 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4305 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4306 if ((appData.animateDragging || appData.highlightDragging)
\r
4307 && (wParam & MK_LBUTTON || dragging == 2)
\r
4308 && dragInfo.from.x >= 0)
\r
4310 BOOL full_repaint = FALSE;
\r
4312 if (appData.animateDragging) {
\r
4313 dragInfo.pos = pt;
\r
4315 if (appData.highlightDragging) {
\r
4316 HoverEvent(highlightInfo.sq[1].x, highlightInfo.sq[1].y, x, y);
\r
4317 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4318 full_repaint = TRUE;
\r
4322 DrawPosition( full_repaint, NULL);
\r
4324 dragInfo.lastpos = dragInfo.pos;
\r
4328 case WM_MOUSEWHEEL: // [DM]
\r
4329 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4330 /* Mouse Wheel is being rolled forward
\r
4331 * Play moves forward
\r
4333 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4334 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4335 /* Mouse Wheel is being rolled backward
\r
4336 * Play moves backward
\r
4338 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4339 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4343 case WM_MBUTTONUP:
\r
4344 case WM_RBUTTONUP:
\r
4346 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4349 case WM_MBUTTONDOWN:
\r
4350 case WM_RBUTTONDOWN:
\r
4353 fromX = fromY = -1;
\r
4354 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4355 dragInfo.start.x = dragInfo.start.y = -1;
\r
4356 dragInfo.from = dragInfo.start;
\r
4357 dragInfo.lastpos = dragInfo.pos;
\r
4358 if (appData.highlightDragging) {
\r
4359 ClearHighlights();
\r
4362 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4363 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4364 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4365 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4366 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4370 DrawPosition(TRUE, NULL);
\r
4372 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4375 if (message == WM_MBUTTONDOWN) {
\r
4376 buttonCount = 3; /* even if system didn't think so */
\r
4377 if (wParam & MK_SHIFT)
\r
4378 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4380 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4381 } else { /* message == WM_RBUTTONDOWN */
\r
4382 /* Just have one menu, on the right button. Windows users don't
\r
4383 think to try the middle one, and sometimes other software steals
\r
4384 it, or it doesn't really exist. */
\r
4385 if(gameInfo.variant != VariantShogi)
\r
4386 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4388 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4392 SetCapture(hwndMain);
\r
4395 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4396 SetupDropMenu(hmenu);
\r
4397 MenuPopup(hwnd, pt, hmenu, -1);
\r
4407 /* Preprocess messages for buttons in main window */
\r
4409 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4411 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4414 for (i=0; i<N_BUTTONS; i++) {
\r
4415 if (buttonDesc[i].id == id) break;
\r
4417 if (i == N_BUTTONS) return 0;
\r
4418 switch (message) {
\r
4423 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4424 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4431 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4434 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4435 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4436 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4437 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4439 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4441 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4442 TypeInEvent((char)wParam);
\r
4448 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4451 static int promoStyle;
\r
4453 /* Process messages for Promotion dialog box */
\r
4455 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4459 switch (message) {
\r
4460 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4461 /* Center the dialog over the application window */
\r
4462 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4463 Translate(hDlg, DLG_PromotionKing);
\r
4464 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4465 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4466 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4467 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4468 SW_SHOW : SW_HIDE);
\r
4469 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4470 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4471 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4472 PieceToChar(WhiteAngel) != '~') ||
\r
4473 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4474 PieceToChar(BlackAngel) != '~') ) ?
\r
4475 SW_SHOW : SW_HIDE);
\r
4476 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4477 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4478 PieceToChar(WhiteMarshall) != '~') ||
\r
4479 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4480 PieceToChar(BlackMarshall) != '~') ) ?
\r
4481 SW_SHOW : SW_HIDE);
\r
4482 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4483 ShowWindow(GetDlgItem(hDlg, PB_Rook), !promoStyle ? SW_SHOW : SW_HIDE);
\r
4484 ShowWindow(GetDlgItem(hDlg, PB_Bishop), !promoStyle ? SW_SHOW : SW_HIDE);
\r
4486 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4487 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4488 SetWindowText(hDlg, "Promote?");
\r
4490 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4491 gameInfo.variant == VariantSuper ?
\r
4492 SW_SHOW : SW_HIDE);
\r
4495 case WM_COMMAND: /* message: received a command */
\r
4496 switch (LOWORD(wParam)) {
\r
4498 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4499 ClearHighlights();
\r
4500 DrawPosition(FALSE, NULL);
\r
4503 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4506 promoChar = promoStyle ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4509 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4510 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4513 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4514 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4516 case PB_Chancellor:
\r
4517 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4519 case PB_Archbishop:
\r
4520 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4523 promoChar = gameInfo.variant == VariantShogi ? '=' : promoStyle ? NULLCHAR :
\r
4524 ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight));
\r
4529 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4530 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4531 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4532 fromX = fromY = -1;
\r
4533 if (!appData.highlightLastMove) {
\r
4534 ClearHighlights();
\r
4535 DrawPosition(FALSE, NULL);
\r
4542 /* Pop up promotion dialog */
\r
4544 PromotionPopup(HWND hwnd)
\r
4548 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4549 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4550 hwnd, (DLGPROC)lpProc);
\r
4551 FreeProcInstance(lpProc);
\r
4555 PromotionPopUp(char choice)
\r
4557 promoStyle = (choice == '+');
\r
4558 DrawPosition(TRUE, NULL);
\r
4559 PromotionPopup(hwndMain);
\r
4563 LoadGameDialog(HWND hwnd, char* title)
\r
4567 char fileTitle[MSG_SIZ];
\r
4568 f = OpenFileDialog(hwnd, "rb", "",
\r
4569 appData.oldSaveStyle ? "gam" : "pgn",
\r
4571 title, &number, fileTitle, NULL);
\r
4573 cmailMsgLoaded = FALSE;
\r
4574 if (number == 0) {
\r
4575 int error = GameListBuild(f);
\r
4577 DisplayError(_("Cannot build game list"), error);
\r
4578 } else if (!ListEmpty(&gameList) &&
\r
4579 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4580 GameListPopUp(f, fileTitle);
\r
4583 GameListDestroy();
\r
4586 LoadGame(f, number, fileTitle, FALSE);
\r
4590 int get_term_width()
\r
4595 HFONT hfont, hold_font;
\r
4600 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4604 // get the text metrics
\r
4605 hdc = GetDC(hText);
\r
4606 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4607 if (consoleCF.dwEffects & CFE_BOLD)
\r
4608 lf.lfWeight = FW_BOLD;
\r
4609 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4610 lf.lfItalic = TRUE;
\r
4611 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4612 lf.lfStrikeOut = TRUE;
\r
4613 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4614 lf.lfUnderline = TRUE;
\r
4615 hfont = CreateFontIndirect(&lf);
\r
4616 hold_font = SelectObject(hdc, hfont);
\r
4617 GetTextMetrics(hdc, &tm);
\r
4618 SelectObject(hdc, hold_font);
\r
4619 DeleteObject(hfont);
\r
4620 ReleaseDC(hText, hdc);
\r
4622 // get the rectangle
\r
4623 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4625 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4628 void UpdateICSWidth(HWND hText)
\r
4630 LONG old_width, new_width;
\r
4632 new_width = get_term_width(hText, FALSE);
\r
4633 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4634 if (new_width != old_width)
\r
4636 ics_update_width(new_width);
\r
4637 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4642 ChangedConsoleFont()
\r
4645 CHARRANGE tmpsel, sel;
\r
4646 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4647 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4648 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4651 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4652 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4653 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4654 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4655 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4656 * size. This was undocumented in the version of MSVC++ that I had
\r
4657 * when I wrote the code, but is apparently documented now.
\r
4659 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4660 cfmt.bCharSet = f->lf.lfCharSet;
\r
4661 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4662 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4663 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4664 /* Why are the following seemingly needed too? */
\r
4665 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4666 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4667 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4669 tmpsel.cpMax = -1; /*999999?*/
\r
4670 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4671 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4672 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4673 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4675 paraf.cbSize = sizeof(paraf);
\r
4676 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4677 paraf.dxStartIndent = 0;
\r
4678 paraf.dxOffset = WRAP_INDENT;
\r
4679 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4680 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4681 UpdateICSWidth(hText);
\r
4684 /*---------------------------------------------------------------------------*\
\r
4686 * Window Proc for main window
\r
4688 \*---------------------------------------------------------------------------*/
\r
4690 /* Process messages for main window, etc. */
\r
4692 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4695 int wmId, wmEvent;
\r
4699 char fileTitle[MSG_SIZ];
\r
4700 static SnapData sd;
\r
4701 static int peek=0;
\r
4703 switch (message) {
\r
4705 case WM_PAINT: /* message: repaint portion of window */
\r
4709 case WM_ERASEBKGND:
\r
4710 if (IsIconic(hwnd)) {
\r
4711 /* Cheat; change the message */
\r
4712 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4714 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4718 case WM_LBUTTONDOWN:
\r
4719 case WM_MBUTTONDOWN:
\r
4720 case WM_RBUTTONDOWN:
\r
4721 case WM_LBUTTONUP:
\r
4722 case WM_MBUTTONUP:
\r
4723 case WM_RBUTTONUP:
\r
4724 case WM_MOUSEMOVE:
\r
4725 case WM_MOUSEWHEEL:
\r
4726 MouseEvent(hwnd, message, wParam, lParam);
\r
4730 if((char)wParam == '\b') {
\r
4731 ForwardEvent(); peek = 0;
\r
4734 JAWS_KBUP_NAVIGATION
\r
4739 if((char)wParam == '\b') {
\r
4740 if(!peek) BackwardEvent(), peek = 1;
\r
4743 JAWS_KBDOWN_NAVIGATION
\r
4749 JAWS_ALT_INTERCEPT
\r
4751 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4752 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4753 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4754 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4756 SendMessage(h, message, wParam, lParam);
\r
4757 } else if(lParam != KF_REPEAT) {
\r
4758 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4759 TypeInEvent((char)wParam);
\r
4760 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4761 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4766 case WM_PALETTECHANGED:
\r
4767 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4769 HDC hdc = GetDC(hwndMain);
\r
4770 SelectPalette(hdc, hPal, TRUE);
\r
4771 nnew = RealizePalette(hdc);
\r
4773 paletteChanged = TRUE;
\r
4775 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4777 ReleaseDC(hwnd, hdc);
\r
4781 case WM_QUERYNEWPALETTE:
\r
4782 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4784 HDC hdc = GetDC(hwndMain);
\r
4785 paletteChanged = FALSE;
\r
4786 SelectPalette(hdc, hPal, FALSE);
\r
4787 nnew = RealizePalette(hdc);
\r
4789 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4791 ReleaseDC(hwnd, hdc);
\r
4796 case WM_COMMAND: /* message: command from application menu */
\r
4797 wmId = LOWORD(wParam);
\r
4798 wmEvent = HIWORD(wParam);
\r
4803 SAY("new game enter a move to play against the computer with white");
\r
4806 case IDM_NewGameFRC:
\r
4807 if( NewGameFRC() == 0 ) {
\r
4812 case IDM_NewVariant:
\r
4813 NewVariantPopup(hwnd);
\r
4816 case IDM_LoadGame:
\r
4817 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4820 case IDM_LoadNextGame:
\r
4824 case IDM_LoadPrevGame:
\r
4828 case IDM_ReloadGame:
\r
4832 case IDM_LoadPosition:
\r
4833 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4834 Reset(FALSE, TRUE);
\r
4837 f = OpenFileDialog(hwnd, "rb", "",
\r
4838 appData.oldSaveStyle ? "pos" : "fen",
\r
4840 _("Load Position from File"), &number, fileTitle, NULL);
\r
4842 LoadPosition(f, number, fileTitle);
\r
4846 case IDM_LoadNextPosition:
\r
4847 ReloadPosition(1);
\r
4850 case IDM_LoadPrevPosition:
\r
4851 ReloadPosition(-1);
\r
4854 case IDM_ReloadPosition:
\r
4855 ReloadPosition(0);
\r
4858 case IDM_SaveGame:
\r
4859 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4860 f = OpenFileDialog(hwnd, "a", defName,
\r
4861 appData.oldSaveStyle ? "gam" : "pgn",
\r
4863 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4865 SaveGame(f, 0, "");
\r
4869 case IDM_SavePosition:
\r
4870 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4871 f = OpenFileDialog(hwnd, "a", defName,
\r
4872 appData.oldSaveStyle ? "pos" : "fen",
\r
4874 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4876 SavePosition(f, 0, "");
\r
4880 case IDM_SaveDiagram:
\r
4881 defName = "diagram";
\r
4882 f = OpenFileDialog(hwnd, "wb", defName,
\r
4885 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
4891 case IDM_CreateBook:
\r
4892 CreateBookEvent();
\r
4895 case IDM_CopyGame:
\r
4896 CopyGameToClipboard();
\r
4899 case IDM_PasteGame:
\r
4900 PasteGameFromClipboard();
\r
4903 case IDM_CopyGameListToClipboard:
\r
4904 CopyGameListToClipboard();
\r
4907 /* [AS] Autodetect FEN or PGN data */
\r
4908 case IDM_PasteAny:
\r
4909 PasteGameOrFENFromClipboard();
\r
4912 /* [AS] Move history */
\r
4913 case IDM_ShowMoveHistory:
\r
4914 if( MoveHistoryIsUp() ) {
\r
4915 MoveHistoryPopDown();
\r
4918 MoveHistoryPopUp();
\r
4922 /* [AS] Eval graph */
\r
4923 case IDM_ShowEvalGraph:
\r
4924 if( EvalGraphIsUp() ) {
\r
4925 EvalGraphPopDown();
\r
4929 SetFocus(hwndMain);
\r
4933 /* [AS] Engine output */
\r
4934 case IDM_ShowEngineOutput:
\r
4935 if( EngineOutputIsUp() ) {
\r
4936 EngineOutputPopDown();
\r
4939 EngineOutputPopUp();
\r
4943 /* [AS] User adjudication */
\r
4944 case IDM_UserAdjudication_White:
\r
4945 UserAdjudicationEvent( +1 );
\r
4948 case IDM_UserAdjudication_Black:
\r
4949 UserAdjudicationEvent( -1 );
\r
4952 case IDM_UserAdjudication_Draw:
\r
4953 UserAdjudicationEvent( 0 );
\r
4956 /* [AS] Game list options dialog */
\r
4957 case IDM_GameListOptions:
\r
4958 GameListOptions();
\r
4965 case IDM_CopyPosition:
\r
4966 CopyFENToClipboard();
\r
4969 case IDM_PastePosition:
\r
4970 PasteFENFromClipboard();
\r
4973 case IDM_MailMove:
\r
4977 case IDM_ReloadCMailMsg:
\r
4978 Reset(TRUE, TRUE);
\r
4979 ReloadCmailMsgEvent(FALSE);
\r
4982 case IDM_Minimize:
\r
4983 ShowWindow(hwnd, SW_MINIMIZE);
\r
4990 case IDM_MachineWhite:
\r
4991 MachineWhiteEvent();
\r
4993 * refresh the tags dialog only if it's visible
\r
4995 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4997 tags = PGNTags(&gameInfo);
\r
4998 TagsPopUp(tags, CmailMsg());
\r
5001 SAY("computer starts playing white");
\r
5004 case IDM_MachineBlack:
\r
5005 MachineBlackEvent();
\r
5007 * refresh the tags dialog only if it's visible
\r
5009 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
5011 tags = PGNTags(&gameInfo);
\r
5012 TagsPopUp(tags, CmailMsg());
\r
5015 SAY("computer starts playing black");
\r
5018 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
5019 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
5022 case IDM_TwoMachines:
\r
5023 TwoMachinesEvent();
\r
5025 * refresh the tags dialog only if it's visible
\r
5027 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
5029 tags = PGNTags(&gameInfo);
\r
5030 TagsPopUp(tags, CmailMsg());
\r
5033 SAY("computer starts playing both sides");
\r
5036 case IDM_AnalysisMode:
\r
5037 if(AnalyzeModeEvent()) {
\r
5038 SAY("analyzing current position");
\r
5042 case IDM_AnalyzeFile:
\r
5043 AnalyzeFileEvent();
\r
5046 case IDM_IcsClient:
\r
5050 case IDM_EditGame:
\r
5051 case IDM_EditGame2:
\r
5056 case IDM_EditPosition:
\r
5057 case IDM_EditPosition2:
\r
5058 EditPositionEvent();
\r
5059 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
5062 case IDM_Training:
\r
5066 case IDM_ShowGameList:
\r
5067 ShowGameListProc();
\r
5070 case IDM_EditProgs1:
\r
5071 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
5074 case IDM_LoadProg1:
\r
5075 LoadEnginePopUp(hwndMain, 0);
\r
5078 case IDM_LoadProg2:
\r
5079 LoadEnginePopUp(hwndMain, 1);
\r
5082 case IDM_EditServers:
\r
5083 EditTagsPopUp(icsNames, &icsNames);
\r
5086 case IDM_EditTags:
\r
5091 case IDM_EditBook:
\r
5095 case IDM_EditComment:
\r
5097 if (commentUp && editComment) {
\r
5100 EditCommentEvent();
\r
5121 case IDM_CallFlag:
\r
5141 case IDM_StopObserving:
\r
5142 StopObservingEvent();
\r
5145 case IDM_StopExamining:
\r
5146 StopExaminingEvent();
\r
5150 UploadGameEvent();
\r
5153 case IDM_TypeInMove:
\r
5154 TypeInEvent('\000');
\r
5157 case IDM_TypeInName:
\r
5158 PopUpNameDialog('\000');
\r
5161 case IDM_Backward:
\r
5163 SetFocus(hwndMain);
\r
5170 SetFocus(hwndMain);
\r
5175 SetFocus(hwndMain);
\r
5180 SetFocus(hwndMain);
\r
5183 case OPT_GameListNext: // [HGM] forward these two accelerators to Game List
\r
5184 case OPT_GameListPrev:
\r
5185 if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);
\r
5189 RevertEvent(FALSE);
\r
5192 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5193 RevertEvent(TRUE);
\r
5196 case IDM_TruncateGame:
\r
5197 TruncateGameEvent();
\r
5204 case IDM_RetractMove:
\r
5205 RetractMoveEvent();
\r
5208 case IDM_FlipView:
\r
5209 flipView = !flipView;
\r
5210 DrawPosition(FALSE, NULL);
\r
5213 case IDM_FlipClock:
\r
5214 flipClock = !flipClock;
\r
5215 DisplayBothClocks();
\r
5219 case IDM_MuteSounds:
\r
5220 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5221 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5222 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5225 case IDM_GeneralOptions:
\r
5226 GeneralOptionsPopup(hwnd);
\r
5227 DrawPosition(TRUE, NULL);
\r
5230 case IDM_BoardOptions:
\r
5231 BoardOptionsPopup(hwnd);
\r
5234 case IDM_ThemeOptions:
\r
5235 ThemeOptionsPopup(hwnd);
\r
5238 case IDM_EnginePlayOptions:
\r
5239 EnginePlayOptionsPopup(hwnd);
\r
5242 case IDM_Engine1Options:
\r
5243 EngineOptionsPopup(hwnd, &first);
\r
5246 case IDM_Engine2Options:
\r
5248 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5249 EngineOptionsPopup(hwnd, &second);
\r
5252 case IDM_OptionsUCI:
\r
5253 UciOptionsPopup(hwnd);
\r
5257 TourneyPopup(hwnd);
\r
5260 case IDM_IcsOptions:
\r
5261 IcsOptionsPopup(hwnd);
\r
5265 FontsOptionsPopup(hwnd);
\r
5269 SoundOptionsPopup(hwnd);
\r
5272 case IDM_CommPort:
\r
5273 CommPortOptionsPopup(hwnd);
\r
5276 case IDM_LoadOptions:
\r
5277 LoadOptionsPopup(hwnd);
\r
5280 case IDM_SaveOptions:
\r
5281 SaveOptionsPopup(hwnd);
\r
5284 case IDM_TimeControl:
\r
5285 TimeControlOptionsPopup(hwnd);
\r
5288 case IDM_SaveSettings:
\r
5289 SaveSettings(settingsFileName);
\r
5292 case IDM_SaveSettingsOnExit:
\r
5293 saveSettingsOnExit = !saveSettingsOnExit;
\r
5294 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5295 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5296 MF_CHECKED : MF_UNCHECKED));
\r
5307 case IDM_AboutGame:
\r
5312 appData.debugMode = !appData.debugMode;
\r
5313 if (appData.debugMode) {
\r
5314 char dir[MSG_SIZ];
\r
5315 GetCurrentDirectory(MSG_SIZ, dir);
\r
5316 SetCurrentDirectory(installDir);
\r
5317 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5318 SetCurrentDirectory(dir);
\r
5319 setbuf(debugFP, NULL);
\r
5326 case IDM_HELPCONTENTS:
\r
5327 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5328 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5329 MessageBox (GetFocus(),
\r
5330 _("Unable to activate help"),
\r
5331 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5335 case IDM_HELPSEARCH:
\r
5336 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5337 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5338 MessageBox (GetFocus(),
\r
5339 _("Unable to activate help"),
\r
5340 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5344 case IDM_HELPHELP:
\r
5345 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5346 MessageBox (GetFocus(),
\r
5347 _("Unable to activate help"),
\r
5348 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5353 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5355 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5356 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5357 FreeProcInstance(lpProc);
\r
5360 case IDM_DirectCommand1:
\r
5361 AskQuestionEvent(_("Direct Command"),
\r
5362 _("Send to chess program:"), "", "1");
\r
5364 case IDM_DirectCommand2:
\r
5365 AskQuestionEvent(_("Direct Command"),
\r
5366 _("Send to second chess program:"), "", "2");
\r
5369 case EP_WhitePawn:
\r
5370 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5371 fromX = fromY = -1;
\r
5374 case EP_WhiteKnight:
\r
5375 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5376 fromX = fromY = -1;
\r
5379 case EP_WhiteBishop:
\r
5380 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5381 fromX = fromY = -1;
\r
5384 case EP_WhiteRook:
\r
5385 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5386 fromX = fromY = -1;
\r
5389 case EP_WhiteQueen:
\r
5390 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5391 fromX = fromY = -1;
\r
5394 case EP_WhiteFerz:
\r
5395 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5396 fromX = fromY = -1;
\r
5399 case EP_WhiteWazir:
\r
5400 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5401 fromX = fromY = -1;
\r
5404 case EP_WhiteAlfil:
\r
5405 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5406 fromX = fromY = -1;
\r
5409 case EP_WhiteCannon:
\r
5410 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5411 fromX = fromY = -1;
\r
5414 case EP_WhiteCardinal:
\r
5415 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5416 fromX = fromY = -1;
\r
5419 case EP_WhiteMarshall:
\r
5420 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5421 fromX = fromY = -1;
\r
5424 case EP_WhiteKing:
\r
5425 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5426 fromX = fromY = -1;
\r
5429 case EP_BlackPawn:
\r
5430 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5431 fromX = fromY = -1;
\r
5434 case EP_BlackKnight:
\r
5435 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5436 fromX = fromY = -1;
\r
5439 case EP_BlackBishop:
\r
5440 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5441 fromX = fromY = -1;
\r
5444 case EP_BlackRook:
\r
5445 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5446 fromX = fromY = -1;
\r
5449 case EP_BlackQueen:
\r
5450 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5451 fromX = fromY = -1;
\r
5454 case EP_BlackFerz:
\r
5455 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5456 fromX = fromY = -1;
\r
5459 case EP_BlackWazir:
\r
5460 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5461 fromX = fromY = -1;
\r
5464 case EP_BlackAlfil:
\r
5465 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5466 fromX = fromY = -1;
\r
5469 case EP_BlackCannon:
\r
5470 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5471 fromX = fromY = -1;
\r
5474 case EP_BlackCardinal:
\r
5475 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5476 fromX = fromY = -1;
\r
5479 case EP_BlackMarshall:
\r
5480 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5481 fromX = fromY = -1;
\r
5484 case EP_BlackKing:
\r
5485 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5486 fromX = fromY = -1;
\r
5489 case EP_EmptySquare:
\r
5490 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5491 fromX = fromY = -1;
\r
5494 case EP_ClearBoard:
\r
5495 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5496 fromX = fromY = -1;
\r
5500 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5501 fromX = fromY = -1;
\r
5505 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5506 fromX = fromY = -1;
\r
5510 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5511 fromX = fromY = -1;
\r
5515 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5516 fromX = fromY = -1;
\r
5520 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5521 fromX = fromY = -1;
\r
5525 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5526 fromX = fromY = -1;
\r
5530 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5531 fromX = fromY = -1;
\r
5535 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5536 fromX = fromY = -1;
\r
5540 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5541 fromX = fromY = -1;
\r
5545 barbaric = 0; appData.language = "";
\r
5546 TranslateMenus(0);
\r
5547 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5548 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5549 lastChecked = wmId;
\r
5553 if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)
\r
5554 RecentEngineEvent(wmId - IDM_RecentEngines);
\r
5556 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5557 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5558 TranslateMenus(0);
\r
5559 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5560 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5561 lastChecked = wmId;
\r
5564 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5570 case CLOCK_TIMER_ID:
\r
5571 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5572 clockTimerEvent = 0;
\r
5573 DecrementClocks(); /* call into back end */
\r
5575 case LOAD_GAME_TIMER_ID:
\r
5576 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5577 loadGameTimerEvent = 0;
\r
5578 AutoPlayGameLoop(); /* call into back end */
\r
5580 case ANALYSIS_TIMER_ID:
\r
5581 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5582 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5583 AnalysisPeriodicEvent(0);
\r
5585 KillTimer(hwnd, analysisTimerEvent);
\r
5586 analysisTimerEvent = 0;
\r
5589 case DELAYED_TIMER_ID:
\r
5590 KillTimer(hwnd, delayedTimerEvent);
\r
5591 delayedTimerEvent = 0;
\r
5592 delayedTimerCallback();
\r
5597 case WM_USER_Input:
\r
5598 InputEvent(hwnd, message, wParam, lParam);
\r
5601 /* [AS] Also move "attached" child windows */
\r
5602 case WM_WINDOWPOSCHANGING:
\r
5604 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5605 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5607 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5608 /* Window is moving */
\r
5611 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5612 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5613 rcMain.right = wpMain.x + wpMain.width;
\r
5614 rcMain.top = wpMain.y;
\r
5615 rcMain.bottom = wpMain.y + wpMain.height;
\r
5617 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5618 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5619 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5620 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5621 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5622 wpMain.x = lpwp->x;
\r
5623 wpMain.y = lpwp->y;
\r
5628 /* [AS] Snapping */
\r
5629 case WM_ENTERSIZEMOVE:
\r
5630 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5631 if (hwnd == hwndMain) {
\r
5632 doingSizing = TRUE;
\r
5635 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5639 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5640 if (hwnd == hwndMain) {
\r
5641 lastSizing = wParam;
\r
5646 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5647 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5649 case WM_EXITSIZEMOVE:
\r
5650 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5651 if (hwnd == hwndMain) {
\r
5653 doingSizing = FALSE;
\r
5654 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5655 GetClientRect(hwnd, &client);
\r
5656 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5658 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5660 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5663 case WM_DESTROY: /* message: window being destroyed */
\r
5664 PostQuitMessage(0);
\r
5668 if (hwnd == hwndMain) {
\r
5673 default: /* Passes it on if unprocessed */
\r
5674 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5679 /*---------------------------------------------------------------------------*\
\r
5681 * Misc utility routines
\r
5683 \*---------------------------------------------------------------------------*/
\r
5686 * Decent random number generator, at least not as bad as Windows
\r
5687 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5689 unsigned int randstate;
\r
5694 randstate = randstate * 1664525 + 1013904223;
\r
5695 return (int) randstate & 0x7fffffff;
\r
5699 mysrandom(unsigned int seed)
\r
5706 * returns TRUE if user selects a different color, FALSE otherwise
\r
5710 ChangeColor(HWND hwnd, COLORREF *which)
\r
5712 static BOOL firstTime = TRUE;
\r
5713 static DWORD customColors[16];
\r
5715 COLORREF newcolor;
\r
5720 /* Make initial colors in use available as custom colors */
\r
5721 /* Should we put the compiled-in defaults here instead? */
\r
5723 customColors[i++] = lightSquareColor & 0xffffff;
\r
5724 customColors[i++] = darkSquareColor & 0xffffff;
\r
5725 customColors[i++] = whitePieceColor & 0xffffff;
\r
5726 customColors[i++] = blackPieceColor & 0xffffff;
\r
5727 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5728 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5730 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5731 customColors[i++] = textAttribs[ccl].color;
\r
5733 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5734 firstTime = FALSE;
\r
5737 cc.lStructSize = sizeof(cc);
\r
5738 cc.hwndOwner = hwnd;
\r
5739 cc.hInstance = NULL;
\r
5740 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5741 cc.lpCustColors = (LPDWORD) customColors;
\r
5742 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5744 if (!ChooseColor(&cc)) return FALSE;
\r
5746 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5747 if (newcolor == *which) return FALSE;
\r
5748 *which = newcolor;
\r
5752 InitDrawingColors();
\r
5753 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5758 MyLoadSound(MySound *ms)
\r
5764 if (ms->data && ms->flag) free(ms->data);
\r
5767 switch (ms->name[0]) {
\r
5773 /* System sound from Control Panel. Don't preload here. */
\r
5777 if (ms->name[1] == NULLCHAR) {
\r
5778 /* "!" alone = silence */
\r
5781 /* Builtin wave resource. Error if not found. */
\r
5782 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5783 if (h == NULL) break;
\r
5784 ms->data = (void *)LoadResource(hInst, h);
\r
5785 ms->flag = 0; // not maloced, so cannot be freed!
\r
5786 if (h == NULL) break;
\r
5791 /* .wav file. Error if not found. */
\r
5792 f = fopen(ms->name, "rb");
\r
5793 if (f == NULL) break;
\r
5794 if (fstat(fileno(f), &st) < 0) break;
\r
5795 ms->data = malloc(st.st_size);
\r
5797 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5803 char buf[MSG_SIZ];
\r
5804 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5805 DisplayError(buf, GetLastError());
\r
5811 MyPlaySound(MySound *ms)
\r
5813 BOOLEAN ok = FALSE;
\r
5815 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5816 switch (ms->name[0]) {
\r
5818 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5823 /* System sound from Control Panel (deprecated feature).
\r
5824 "$" alone or an unset sound name gets default beep (still in use). */
\r
5825 if (ms->name[1]) {
\r
5826 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5828 if (!ok) ok = MessageBeep(MB_OK);
\r
5831 /* Builtin wave resource, or "!" alone for silence */
\r
5832 if (ms->name[1]) {
\r
5833 if (ms->data == NULL) return FALSE;
\r
5834 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5840 /* .wav file. Error if not found. */
\r
5841 if (ms->data == NULL) return FALSE;
\r
5842 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5845 /* Don't print an error: this can happen innocently if the sound driver
\r
5846 is busy; for instance, if another instance of WinBoard is playing
\r
5847 a sound at about the same time. */
\r
5853 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5856 OPENFILENAME *ofn;
\r
5857 static UINT *number; /* gross that this is static */
\r
5859 switch (message) {
\r
5860 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5861 /* Center the dialog over the application window */
\r
5862 ofn = (OPENFILENAME *) lParam;
\r
5863 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5864 number = (UINT *) ofn->lCustData;
\r
5865 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5869 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5870 Translate(hDlg, 1536);
\r
5871 return FALSE; /* Allow for further processing */
\r
5874 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5875 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5877 return FALSE; /* Allow for further processing */
\r
5883 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5885 static UINT *number;
\r
5886 OPENFILENAME *ofname;
\r
5889 case WM_INITDIALOG:
\r
5890 Translate(hdlg, DLG_IndexNumber);
\r
5891 ofname = (OPENFILENAME *)lParam;
\r
5892 number = (UINT *)(ofname->lCustData);
\r
5895 ofnot = (OFNOTIFY *)lParam;
\r
5896 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5897 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5906 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5907 char *nameFilt, char *dlgTitle, UINT *number,
\r
5908 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5910 OPENFILENAME openFileName;
\r
5911 char buf1[MSG_SIZ];
\r
5914 if (fileName == NULL) fileName = buf1;
\r
5915 if (defName == NULL) {
\r
5916 safeStrCpy(fileName, "*.", 3 );
\r
5917 strcat(fileName, defExt);
\r
5919 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5921 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5922 if (number) *number = 0;
\r
5924 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5925 openFileName.hwndOwner = hwnd;
\r
5926 openFileName.hInstance = (HANDLE) hInst;
\r
5927 openFileName.lpstrFilter = nameFilt;
\r
5928 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5929 openFileName.nMaxCustFilter = 0L;
\r
5930 openFileName.nFilterIndex = 1L;
\r
5931 openFileName.lpstrFile = fileName;
\r
5932 openFileName.nMaxFile = MSG_SIZ;
\r
5933 openFileName.lpstrFileTitle = fileTitle;
\r
5934 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5935 openFileName.lpstrInitialDir = NULL;
\r
5936 openFileName.lpstrTitle = dlgTitle;
\r
5937 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5938 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5939 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5940 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5941 openFileName.nFileOffset = 0;
\r
5942 openFileName.nFileExtension = 0;
\r
5943 openFileName.lpstrDefExt = defExt;
\r
5944 openFileName.lCustData = (LONG) number;
\r
5945 openFileName.lpfnHook = oldDialog ?
\r
5946 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5947 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5949 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5950 GetOpenFileName(&openFileName)) {
\r
5951 /* open the file */
\r
5952 f = fopen(openFileName.lpstrFile, write);
\r
5954 MessageBox(hwnd, _("File open failed"), NULL,
\r
5955 MB_OK|MB_ICONEXCLAMATION);
\r
5959 int err = CommDlgExtendedError();
\r
5960 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5969 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5971 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5974 * Get the first pop-up menu in the menu template. This is the
\r
5975 * menu that TrackPopupMenu displays.
\r
5977 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5978 TranslateOneMenu(10, hmenuTrackPopup);
\r
5980 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5983 * TrackPopup uses screen coordinates, so convert the
\r
5984 * coordinates of the mouse click to screen coordinates.
\r
5986 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5988 /* Draw and track the floating pop-up menu. */
\r
5989 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5990 pt.x, pt.y, 0, hwnd, NULL);
\r
5992 /* Destroy the menu.*/
\r
5993 DestroyMenu(hmenu);
\r
5998 int sizeX, sizeY, newSizeX, newSizeY;
\r
6000 } ResizeEditPlusButtonsClosure;
\r
6003 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
6005 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
6009 if (hChild == cl->hText) return TRUE;
\r
6010 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
6011 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
6012 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
6013 ScreenToClient(cl->hDlg, &pt);
\r
6014 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
6015 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
6019 /* Resize a dialog that has a (rich) edit field filling most of
\r
6020 the top, with a row of buttons below */
\r
6022 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
6025 int newTextHeight, newTextWidth;
\r
6026 ResizeEditPlusButtonsClosure cl;
\r
6028 /*if (IsIconic(hDlg)) return;*/
\r
6029 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
6031 cl.hdwp = BeginDeferWindowPos(8);
\r
6033 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
6034 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6035 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6036 if (newTextHeight < 0) {
\r
6037 newSizeY += -newTextHeight;
\r
6038 newTextHeight = 0;
\r
6040 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
6041 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6047 cl.newSizeX = newSizeX;
\r
6048 cl.newSizeY = newSizeY;
\r
6049 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
6051 EndDeferWindowPos(cl.hdwp);
\r
6054 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
6056 RECT rChild, rParent;
\r
6057 int wChild, hChild, wParent, hParent;
\r
6058 int wScreen, hScreen, xNew, yNew;
\r
6061 /* Get the Height and Width of the child window */
\r
6062 GetWindowRect (hwndChild, &rChild);
\r
6063 wChild = rChild.right - rChild.left;
\r
6064 hChild = rChild.bottom - rChild.top;
\r
6066 /* Get the Height and Width of the parent window */
\r
6067 GetWindowRect (hwndParent, &rParent);
\r
6068 wParent = rParent.right - rParent.left;
\r
6069 hParent = rParent.bottom - rParent.top;
\r
6071 /* Get the display limits */
\r
6072 hdc = GetDC (hwndChild);
\r
6073 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
6074 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
6075 ReleaseDC(hwndChild, hdc);
\r
6077 /* Calculate new X position, then adjust for screen */
\r
6078 xNew = rParent.left + ((wParent - wChild) /2);
\r
6081 } else if ((xNew+wChild) > wScreen) {
\r
6082 xNew = wScreen - wChild;
\r
6085 /* Calculate new Y position, then adjust for screen */
\r
6087 yNew = rParent.top + ((hParent - hChild) /2);
\r
6090 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
6095 } else if ((yNew+hChild) > hScreen) {
\r
6096 yNew = hScreen - hChild;
\r
6099 /* Set it, and return */
\r
6100 return SetWindowPos (hwndChild, NULL,
\r
6101 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
6104 /* Center one window over another */
\r
6105 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
6107 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
6110 /*---------------------------------------------------------------------------*\
\r
6112 * Startup Dialog functions
\r
6114 \*---------------------------------------------------------------------------*/
\r
6116 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
6118 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6120 while (*cd != NULL) {
\r
6121 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
6127 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
6129 char buf1[MAX_ARG_LEN];
\r
6132 if (str[0] == '@') {
\r
6133 FILE* f = fopen(str + 1, "r");
\r
6135 DisplayFatalError(str + 1, errno, 2);
\r
6138 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
6140 buf1[len] = NULLCHAR;
\r
6144 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6147 char buf[MSG_SIZ];
\r
6148 char *end = strchr(str, '\n');
\r
6149 if (end == NULL) return;
\r
6150 memcpy(buf, str, end - str);
\r
6151 buf[end - str] = NULLCHAR;
\r
6152 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6158 SetStartupDialogEnables(HWND hDlg)
\r
6160 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6161 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6162 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6163 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6164 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6165 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6166 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6167 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6168 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6169 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6170 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6171 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6172 IsDlgButtonChecked(hDlg, OPT_View));
\r
6176 QuoteForFilename(char *filename)
\r
6178 int dquote, space;
\r
6179 dquote = strchr(filename, '"') != NULL;
\r
6180 space = strchr(filename, ' ') != NULL;
\r
6181 if (dquote || space) {
\r
6193 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6195 char buf[MSG_SIZ];
\r
6198 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6199 q = QuoteForFilename(nthcp);
\r
6200 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6201 if (*nthdir != NULLCHAR) {
\r
6202 q = QuoteForFilename(nthdir);
\r
6203 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6205 if (*nthcp == NULLCHAR) {
\r
6206 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6207 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6208 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6209 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6214 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6216 char buf[MSG_SIZ];
\r
6220 switch (message) {
\r
6221 case WM_INITDIALOG:
\r
6222 /* Center the dialog */
\r
6223 CenterWindow (hDlg, GetDesktopWindow());
\r
6224 Translate(hDlg, DLG_Startup);
\r
6225 /* Initialize the dialog items */
\r
6226 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6227 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6228 firstChessProgramNames);
\r
6229 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6230 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6231 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6232 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6233 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6234 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6235 if (*appData.icsHelper != NULLCHAR) {
\r
6236 char *q = QuoteForFilename(appData.icsHelper);
\r
6237 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6239 if (*appData.icsHost == NULLCHAR) {
\r
6240 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6241 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6242 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6243 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6244 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6247 if (appData.icsActive) {
\r
6248 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6250 else if (appData.noChessProgram) {
\r
6251 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6254 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6257 SetStartupDialogEnables(hDlg);
\r
6261 switch (LOWORD(wParam)) {
\r
6263 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6264 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6265 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6267 comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox
\r
6268 ParseArgs(StringGet, &p);
\r
6269 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6270 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6272 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6273 ParseArgs(StringGet, &p);
\r
6274 SwapEngines(singleList); // ... and then make it 'second'
\r
6276 appData.noChessProgram = FALSE;
\r
6277 appData.icsActive = FALSE;
\r
6278 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6279 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6280 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6282 ParseArgs(StringGet, &p);
\r
6283 if (appData.zippyPlay) {
\r
6284 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6285 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6287 ParseArgs(StringGet, &p);
\r
6289 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6290 appData.noChessProgram = TRUE;
\r
6291 appData.icsActive = FALSE;
\r
6293 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6294 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6297 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6298 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6300 ParseArgs(StringGet, &p);
\r
6302 EndDialog(hDlg, TRUE);
\r
6309 case IDM_HELPCONTENTS:
\r
6310 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6311 MessageBox (GetFocus(),
\r
6312 _("Unable to activate help"),
\r
6313 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6318 SetStartupDialogEnables(hDlg);
\r
6326 /*---------------------------------------------------------------------------*\
\r
6328 * About box dialog functions
\r
6330 \*---------------------------------------------------------------------------*/
\r
6332 /* Process messages for "About" dialog box */
\r
6334 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6336 switch (message) {
\r
6337 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6338 /* Center the dialog over the application window */
\r
6339 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6340 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6341 Translate(hDlg, ABOUTBOX);
\r
6345 case WM_COMMAND: /* message: received a command */
\r
6346 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6347 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6348 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6356 /*---------------------------------------------------------------------------*\
\r
6358 * Comment Dialog functions
\r
6360 \*---------------------------------------------------------------------------*/
\r
6363 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6365 static HANDLE hwndText = NULL;
\r
6366 int len, newSizeX, newSizeY, flags;
\r
6367 static int sizeX, sizeY;
\r
6372 switch (message) {
\r
6373 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6374 /* Initialize the dialog items */
\r
6375 Translate(hDlg, DLG_EditComment);
\r
6376 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6377 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6378 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6379 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6380 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6381 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6382 SetWindowText(hDlg, commentTitle);
\r
6383 if (editComment) {
\r
6384 SetFocus(hwndText);
\r
6386 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6388 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6389 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6390 MAKELPARAM(FALSE, 0));
\r
6391 /* Size and position the dialog */
\r
6392 if (!commentDialog) {
\r
6393 commentDialog = hDlg;
\r
6394 flags = SWP_NOZORDER;
\r
6395 GetClientRect(hDlg, &rect);
\r
6396 sizeX = rect.right;
\r
6397 sizeY = rect.bottom;
\r
6398 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6399 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6400 WINDOWPLACEMENT wp;
\r
6401 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6402 wp.length = sizeof(WINDOWPLACEMENT);
\r
6404 wp.showCmd = SW_SHOW;
\r
6405 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6406 wp.rcNormalPosition.left = wpComment.x;
\r
6407 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6408 wp.rcNormalPosition.top = wpComment.y;
\r
6409 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6410 SetWindowPlacement(hDlg, &wp);
\r
6412 GetClientRect(hDlg, &rect);
\r
6413 newSizeX = rect.right;
\r
6414 newSizeY = rect.bottom;
\r
6415 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6416 newSizeX, newSizeY);
\r
6421 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6424 case WM_COMMAND: /* message: received a command */
\r
6425 switch (LOWORD(wParam)) {
\r
6427 if (editComment) {
\r
6429 /* Read changed options from the dialog box */
\r
6430 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6431 len = GetWindowTextLength(hwndText);
\r
6432 str = (char *) malloc(len + 1);
\r
6433 GetWindowText(hwndText, str, len + 1);
\r
6442 ReplaceComment(commentIndex, str);
\r
6449 case OPT_CancelComment:
\r
6453 case OPT_ClearComment:
\r
6454 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6457 case OPT_EditComment:
\r
6458 EditCommentEvent();
\r
6466 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6467 if( wParam == OPT_CommentText ) {
\r
6468 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6470 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6471 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6475 pt.x = LOWORD( lpMF->lParam );
\r
6476 pt.y = HIWORD( lpMF->lParam );
\r
6478 if(lpMF->msg == WM_CHAR) {
\r
6480 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6481 index = sel.cpMin;
\r
6483 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6485 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6486 len = GetWindowTextLength(hwndText);
\r
6487 str = (char *) malloc(len + 1);
\r
6488 GetWindowText(hwndText, str, len + 1);
\r
6489 ReplaceComment(commentIndex, str);
\r
6490 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6491 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6494 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6495 lpMF->msg = WM_USER;
\r
6503 newSizeX = LOWORD(lParam);
\r
6504 newSizeY = HIWORD(lParam);
\r
6505 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6510 case WM_GETMINMAXINFO:
\r
6511 /* Prevent resizing window too small */
\r
6512 mmi = (MINMAXINFO *) lParam;
\r
6513 mmi->ptMinTrackSize.x = 100;
\r
6514 mmi->ptMinTrackSize.y = 100;
\r
6521 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6526 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6528 if (str == NULL) str = "";
\r
6529 p = (char *) malloc(2 * strlen(str) + 2);
\r
6532 if (*str == '\n') *q++ = '\r';
\r
6536 if (commentText != NULL) free(commentText);
\r
6538 commentIndex = index;
\r
6539 commentTitle = title;
\r
6541 editComment = edit;
\r
6543 if (commentDialog) {
\r
6544 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6545 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6547 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6548 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6549 hwndMain, (DLGPROC)lpProc);
\r
6550 FreeProcInstance(lpProc);
\r
6556 /*---------------------------------------------------------------------------*\
\r
6558 * Type-in move dialog functions
\r
6560 \*---------------------------------------------------------------------------*/
\r
6563 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6565 char move[MSG_SIZ];
\r
6568 switch (message) {
\r
6569 case WM_INITDIALOG:
\r
6570 move[0] = (char) lParam;
\r
6571 move[1] = NULLCHAR;
\r
6572 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6573 Translate(hDlg, DLG_TypeInMove);
\r
6574 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6575 SetWindowText(hInput, move);
\r
6577 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6581 switch (LOWORD(wParam)) {
\r
6584 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6585 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6586 TypeInDoneEvent(move);
\r
6587 EndDialog(hDlg, TRUE);
\r
6590 EndDialog(hDlg, FALSE);
\r
6601 PopUpMoveDialog(char firstchar)
\r
6605 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6606 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6607 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6608 FreeProcInstance(lpProc);
\r
6611 /*---------------------------------------------------------------------------*\
\r
6613 * Type-in name dialog functions
\r
6615 \*---------------------------------------------------------------------------*/
\r
6618 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6620 char move[MSG_SIZ];
\r
6623 switch (message) {
\r
6624 case WM_INITDIALOG:
\r
6625 move[0] = (char) lParam;
\r
6626 move[1] = NULLCHAR;
\r
6627 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6628 Translate(hDlg, DLG_TypeInName);
\r
6629 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6630 SetWindowText(hInput, move);
\r
6632 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6636 switch (LOWORD(wParam)) {
\r
6638 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6639 appData.userName = strdup(move);
\r
6642 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6643 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6644 DisplayTitle(move);
\r
6648 EndDialog(hDlg, TRUE);
\r
6651 EndDialog(hDlg, FALSE);
\r
6662 PopUpNameDialog(char firstchar)
\r
6666 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6667 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6668 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6669 FreeProcInstance(lpProc);
\r
6672 /*---------------------------------------------------------------------------*\
\r
6676 \*---------------------------------------------------------------------------*/
\r
6678 /* Nonmodal error box */
\r
6679 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6680 WPARAM wParam, LPARAM lParam);
\r
6683 ErrorPopUp(char *title, char *content)
\r
6687 BOOLEAN modal = hwndMain == NULL;
\r
6705 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6706 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6709 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6711 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6712 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6713 hwndMain, (DLGPROC)lpProc);
\r
6714 FreeProcInstance(lpProc);
\r
6721 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6722 if (errorDialog == NULL) return;
\r
6723 DestroyWindow(errorDialog);
\r
6724 errorDialog = NULL;
\r
6725 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6729 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6734 switch (message) {
\r
6735 case WM_INITDIALOG:
\r
6736 GetWindowRect(hDlg, &rChild);
\r
6739 SetWindowPos(hDlg, NULL, rChild.left,
\r
6740 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6741 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6745 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6746 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6747 and it doesn't work when you resize the dialog.
\r
6748 For now, just give it a default position.
\r
6750 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6751 Translate(hDlg, DLG_Error);
\r
6753 errorDialog = hDlg;
\r
6754 SetWindowText(hDlg, errorTitle);
\r
6755 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6756 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6760 switch (LOWORD(wParam)) {
\r
6763 if (errorDialog == hDlg) errorDialog = NULL;
\r
6764 DestroyWindow(hDlg);
\r
6776 HWND gothicDialog = NULL;
\r
6779 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6783 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6785 switch (message) {
\r
6786 case WM_INITDIALOG:
\r
6787 GetWindowRect(hDlg, &rChild);
\r
6789 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6793 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6794 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6795 and it doesn't work when you resize the dialog.
\r
6796 For now, just give it a default position.
\r
6798 gothicDialog = hDlg;
\r
6799 SetWindowText(hDlg, errorTitle);
\r
6800 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6801 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6805 switch (LOWORD(wParam)) {
\r
6808 if (errorDialog == hDlg) errorDialog = NULL;
\r
6809 DestroyWindow(hDlg);
\r
6821 GothicPopUp(char *title, VariantClass variant)
\r
6824 static char *lastTitle;
\r
6826 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6827 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6829 if(lastTitle != title && gothicDialog != NULL) {
\r
6830 DestroyWindow(gothicDialog);
\r
6831 gothicDialog = NULL;
\r
6833 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6834 title = lastTitle;
\r
6835 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6836 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6837 hwndMain, (DLGPROC)lpProc);
\r
6838 FreeProcInstance(lpProc);
\r
6843 /*---------------------------------------------------------------------------*\
\r
6845 * Ics Interaction console functions
\r
6847 \*---------------------------------------------------------------------------*/
\r
6849 #define HISTORY_SIZE 64
\r
6850 static char *history[HISTORY_SIZE];
\r
6851 int histIn = 0, histP = 0;
\r
6855 SaveInHistory(char *cmd)
\r
6857 if (history[histIn] != NULL) {
\r
6858 free(history[histIn]);
\r
6859 history[histIn] = NULL;
\r
6861 if (*cmd == NULLCHAR) return;
\r
6862 history[histIn] = StrSave(cmd);
\r
6863 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6864 if (history[histIn] != NULL) {
\r
6865 free(history[histIn]);
\r
6867 history[histIn] = NULL;
\r
6873 PrevInHistory(char *cmd)
\r
6876 if (histP == histIn) {
\r
6877 if (history[histIn] != NULL) free(history[histIn]);
\r
6878 history[histIn] = StrSave(cmd);
\r
6880 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6881 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6883 return history[histP];
\r
6889 if (histP == histIn) return NULL;
\r
6890 histP = (histP + 1) % HISTORY_SIZE;
\r
6891 return history[histP];
\r
6895 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6899 hmenu = LoadMenu(hInst, "TextMenu");
\r
6900 h = GetSubMenu(hmenu, 0);
\r
6902 if (strcmp(e->item, "-") == 0) {
\r
6903 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6904 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6905 int flags = MF_STRING, j = 0;
\r
6906 if (e->item[0] == '|') {
\r
6907 flags |= MF_MENUBARBREAK;
\r
6910 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6911 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6919 WNDPROC consoleTextWindowProc;
\r
6922 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6924 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6925 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6929 SetWindowText(hInput, command);
\r
6931 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6933 sel.cpMin = 999999;
\r
6934 sel.cpMax = 999999;
\r
6935 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6940 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6941 if (sel.cpMin == sel.cpMax) {
\r
6942 /* Expand to surrounding word */
\r
6945 tr.chrg.cpMax = sel.cpMin;
\r
6946 tr.chrg.cpMin = --sel.cpMin;
\r
6947 if (sel.cpMin < 0) break;
\r
6948 tr.lpstrText = name;
\r
6949 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6950 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6954 tr.chrg.cpMin = sel.cpMax;
\r
6955 tr.chrg.cpMax = ++sel.cpMax;
\r
6956 tr.lpstrText = name;
\r
6957 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6958 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6961 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6962 MessageBeep(MB_ICONEXCLAMATION);
\r
6966 tr.lpstrText = name;
\r
6967 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6969 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6970 MessageBeep(MB_ICONEXCLAMATION);
\r
6973 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6976 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
6977 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6978 SetWindowText(hInput, buf);
\r
6979 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6981 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6982 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6983 SetWindowText(hInput, buf);
\r
6984 sel.cpMin = 999999;
\r
6985 sel.cpMax = 999999;
\r
6986 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6992 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6997 switch (message) {
\r
6999 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7000 if(wParam=='R') return 0;
\r
7003 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
7006 sel.cpMin = 999999;
\r
7007 sel.cpMax = 999999;
\r
7008 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7009 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
7014 if(wParam != '\022') {
\r
7015 if (wParam == '\t') {
\r
7016 if (GetKeyState(VK_SHIFT) < 0) {
\r
7018 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7019 if (buttonDesc[0].hwnd) {
\r
7020 SetFocus(buttonDesc[0].hwnd);
\r
7022 SetFocus(hwndMain);
\r
7026 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
7029 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7030 JAWS_DELETE( SetFocus(hInput); )
\r
7031 SendMessage(hInput, message, wParam, lParam);
\r
7034 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
7036 case WM_RBUTTONDOWN:
\r
7037 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
7038 /* Move selection here if it was empty */
\r
7040 pt.x = LOWORD(lParam);
\r
7041 pt.y = HIWORD(lParam);
\r
7042 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7043 if (sel.cpMin == sel.cpMax) {
\r
7044 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
7045 sel.cpMax = sel.cpMin;
\r
7046 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7048 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
7049 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
7051 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
7052 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7053 if (sel.cpMin == sel.cpMax) {
\r
7054 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7055 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
7057 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7058 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7060 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
7061 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
7062 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
7063 MenuPopup(hwnd, pt, hmenu, -1);
\r
7067 case WM_RBUTTONUP:
\r
7068 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7069 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7070 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7074 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7076 return SendMessage(hInput, message, wParam, lParam);
\r
7077 case WM_MBUTTONDOWN:
\r
7078 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7080 switch (LOWORD(wParam)) {
\r
7081 case IDM_QuickPaste:
\r
7083 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7084 if (sel.cpMin == sel.cpMax) {
\r
7085 MessageBeep(MB_ICONEXCLAMATION);
\r
7088 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7089 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7090 SendMessage(hInput, WM_PASTE, 0, 0);
\r
7095 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7098 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7101 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7105 int i = LOWORD(wParam) - IDM_CommandX;
\r
7106 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
7107 icsTextMenuEntry[i].command != NULL) {
\r
7108 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
7109 icsTextMenuEntry[i].getname,
\r
7110 icsTextMenuEntry[i].immediate);
\r
7118 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
7121 WNDPROC consoleInputWindowProc;
\r
7124 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7126 char buf[MSG_SIZ];
\r
7128 static BOOL sendNextChar = FALSE;
\r
7129 static BOOL quoteNextChar = FALSE;
\r
7130 InputSource *is = consoleInputSource;
\r
7134 switch (message) {
\r
7136 if (!appData.localLineEditing || sendNextChar) {
\r
7137 is->buf[0] = (CHAR) wParam;
\r
7139 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7140 sendNextChar = FALSE;
\r
7143 if (quoteNextChar) {
\r
7144 buf[0] = (char) wParam;
\r
7145 buf[1] = NULLCHAR;
\r
7146 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7147 quoteNextChar = FALSE;
\r
7151 case '\r': /* Enter key */
\r
7152 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7153 if (consoleEcho) SaveInHistory(is->buf);
\r
7154 is->buf[is->count++] = '\n';
\r
7155 is->buf[is->count] = NULLCHAR;
\r
7156 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7157 if (consoleEcho) {
\r
7158 ConsoleOutput(is->buf, is->count, TRUE);
\r
7159 } else if (appData.localLineEditing) {
\r
7160 ConsoleOutput("\n", 1, TRUE);
\r
7163 case '\033': /* Escape key */
\r
7164 SetWindowText(hwnd, "");
\r
7165 cf.cbSize = sizeof(CHARFORMAT);
\r
7166 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7167 if (consoleEcho) {
\r
7168 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7170 cf.crTextColor = COLOR_ECHOOFF;
\r
7172 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7173 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7175 case '\t': /* Tab key */
\r
7176 if (GetKeyState(VK_SHIFT) < 0) {
\r
7178 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7181 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7182 if (buttonDesc[0].hwnd) {
\r
7183 SetFocus(buttonDesc[0].hwnd);
\r
7185 SetFocus(hwndMain);
\r
7189 case '\023': /* Ctrl+S */
\r
7190 sendNextChar = TRUE;
\r
7192 case '\021': /* Ctrl+Q */
\r
7193 quoteNextChar = TRUE;
\r
7203 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7204 p = PrevInHistory(buf);
\r
7206 SetWindowText(hwnd, p);
\r
7207 sel.cpMin = 999999;
\r
7208 sel.cpMax = 999999;
\r
7209 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7214 p = NextInHistory();
\r
7216 SetWindowText(hwnd, p);
\r
7217 sel.cpMin = 999999;
\r
7218 sel.cpMax = 999999;
\r
7219 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7225 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7229 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7233 case WM_MBUTTONDOWN:
\r
7234 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7235 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7237 case WM_RBUTTONUP:
\r
7238 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7239 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7240 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7244 hmenu = LoadMenu(hInst, "InputMenu");
\r
7245 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7246 if (sel.cpMin == sel.cpMax) {
\r
7247 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7248 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7250 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7251 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7253 pt.x = LOWORD(lParam);
\r
7254 pt.y = HIWORD(lParam);
\r
7255 MenuPopup(hwnd, pt, hmenu, -1);
\r
7259 switch (LOWORD(wParam)) {
\r
7261 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7263 case IDM_SelectAll:
\r
7265 sel.cpMax = -1; /*999999?*/
\r
7266 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7269 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7272 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7275 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7280 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7283 #define CO_MAX 100000
\r
7284 #define CO_TRIM 1000
\r
7287 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7289 static SnapData sd;
\r
7290 HWND hText, hInput;
\r
7292 static int sizeX, sizeY;
\r
7293 int newSizeX, newSizeY;
\r
7297 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7298 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7300 switch (message) {
\r
7302 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7304 ENLINK *pLink = (ENLINK*)lParam;
\r
7305 if (pLink->msg == WM_LBUTTONUP)
\r
7309 tr.chrg = pLink->chrg;
\r
7310 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7311 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7312 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7313 free(tr.lpstrText);
\r
7317 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7318 hwndConsole = hDlg;
\r
7320 consoleTextWindowProc = (WNDPROC)
\r
7321 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7322 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7323 consoleInputWindowProc = (WNDPROC)
\r
7324 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7325 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7326 Colorize(ColorNormal, TRUE);
\r
7327 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7328 ChangedConsoleFont();
\r
7329 GetClientRect(hDlg, &rect);
\r
7330 sizeX = rect.right;
\r
7331 sizeY = rect.bottom;
\r
7332 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7333 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7334 WINDOWPLACEMENT wp;
\r
7335 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7336 wp.length = sizeof(WINDOWPLACEMENT);
\r
7338 wp.showCmd = SW_SHOW;
\r
7339 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7340 wp.rcNormalPosition.left = wpConsole.x;
\r
7341 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7342 wp.rcNormalPosition.top = wpConsole.y;
\r
7343 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7344 SetWindowPlacement(hDlg, &wp);
\r
7347 // [HGM] Chessknight's change 2004-07-13
\r
7348 else { /* Determine Defaults */
\r
7349 WINDOWPLACEMENT wp;
\r
7350 wpConsole.x = wpMain.width + 1;
\r
7351 wpConsole.y = wpMain.y;
\r
7352 wpConsole.width = screenWidth - wpMain.width;
\r
7353 wpConsole.height = wpMain.height;
\r
7354 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7355 wp.length = sizeof(WINDOWPLACEMENT);
\r
7357 wp.showCmd = SW_SHOW;
\r
7358 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7359 wp.rcNormalPosition.left = wpConsole.x;
\r
7360 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7361 wp.rcNormalPosition.top = wpConsole.y;
\r
7362 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7363 SetWindowPlacement(hDlg, &wp);
\r
7366 // Allow hText to highlight URLs and send notifications on them
\r
7367 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7368 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7369 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7370 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7384 if (IsIconic(hDlg)) break;
\r
7385 newSizeX = LOWORD(lParam);
\r
7386 newSizeY = HIWORD(lParam);
\r
7387 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7388 RECT rectText, rectInput;
\r
7390 int newTextHeight, newTextWidth;
\r
7391 GetWindowRect(hText, &rectText);
\r
7392 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7393 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7394 if (newTextHeight < 0) {
\r
7395 newSizeY += -newTextHeight;
\r
7396 newTextHeight = 0;
\r
7398 SetWindowPos(hText, NULL, 0, 0,
\r
7399 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7400 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7401 pt.x = rectInput.left;
\r
7402 pt.y = rectInput.top + newSizeY - sizeY;
\r
7403 ScreenToClient(hDlg, &pt);
\r
7404 SetWindowPos(hInput, NULL,
\r
7405 pt.x, pt.y, /* needs client coords */
\r
7406 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7407 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7413 case WM_GETMINMAXINFO:
\r
7414 /* Prevent resizing window too small */
\r
7415 mmi = (MINMAXINFO *) lParam;
\r
7416 mmi->ptMinTrackSize.x = 100;
\r
7417 mmi->ptMinTrackSize.y = 100;
\r
7420 /* [AS] Snapping */
\r
7421 case WM_ENTERSIZEMOVE:
\r
7422 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7425 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7428 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7430 case WM_EXITSIZEMOVE:
\r
7431 UpdateICSWidth(hText);
\r
7432 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7435 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7443 if (hwndConsole) return;
\r
7444 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7445 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7450 ConsoleOutput(char* data, int length, int forceVisible)
\r
7455 char buf[CO_MAX+1];
\r
7458 static int delayLF = 0;
\r
7459 CHARRANGE savesel, sel;
\r
7461 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7469 while (length--) {
\r
7477 } else if (*p == '\007') {
\r
7478 MyPlaySound(&sounds[(int)SoundBell]);
\r
7485 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7486 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7487 /* Save current selection */
\r
7488 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7489 exlen = GetWindowTextLength(hText);
\r
7490 /* Find out whether current end of text is visible */
\r
7491 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7492 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7493 /* Trim existing text if it's too long */
\r
7494 if (exlen + (q - buf) > CO_MAX) {
\r
7495 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7498 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7499 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7501 savesel.cpMin -= trim;
\r
7502 savesel.cpMax -= trim;
\r
7503 if (exlen < 0) exlen = 0;
\r
7504 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7505 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7507 /* Append the new text */
\r
7508 sel.cpMin = exlen;
\r
7509 sel.cpMax = exlen;
\r
7510 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7511 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7512 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7513 if (forceVisible || exlen == 0 ||
\r
7514 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7515 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7516 /* Scroll to make new end of text visible if old end of text
\r
7517 was visible or new text is an echo of user typein */
\r
7518 sel.cpMin = 9999999;
\r
7519 sel.cpMax = 9999999;
\r
7520 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7521 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7522 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7523 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7525 if (savesel.cpMax == exlen || forceVisible) {
\r
7526 /* Move insert point to new end of text if it was at the old
\r
7527 end of text or if the new text is an echo of user typein */
\r
7528 sel.cpMin = 9999999;
\r
7529 sel.cpMax = 9999999;
\r
7530 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7532 /* Restore previous selection */
\r
7533 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7535 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7542 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7546 COLORREF oldFg, oldBg;
\r
7550 if(copyNumber > 1)
\r
7551 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7553 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7554 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7555 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7558 rect.right = x + squareSize;
\r
7560 rect.bottom = y + squareSize;
\r
7563 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7564 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7565 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7566 &rect, str, strlen(str), NULL);
\r
7568 (void) SetTextColor(hdc, oldFg);
\r
7569 (void) SetBkColor(hdc, oldBg);
\r
7570 (void) SelectObject(hdc, oldFont);
\r
7574 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7575 RECT *rect, char *color, char *flagFell)
\r
7579 COLORREF oldFg, oldBg;
\r
7582 if (twoBoards && partnerUp) return;
\r
7583 if (appData.clockMode) {
\r
7585 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7587 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7594 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7595 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7597 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7598 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7600 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7604 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7605 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7606 rect, str, strlen(str), NULL);
\r
7607 if(logoHeight > 0 && appData.clockMode) {
\r
7609 str += strlen(color)+2;
\r
7610 r.top = rect->top + logoHeight/2;
\r
7611 r.left = rect->left;
\r
7612 r.right = rect->right;
\r
7613 r.bottom = rect->bottom;
\r
7614 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7615 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7616 &r, str, strlen(str), NULL);
\r
7618 (void) SetTextColor(hdc, oldFg);
\r
7619 (void) SetBkColor(hdc, oldBg);
\r
7620 (void) SelectObject(hdc, oldFont);
\r
7625 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7631 if( count <= 0 ) {
\r
7632 if (appData.debugMode) {
\r
7633 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7636 return ERROR_INVALID_USER_BUFFER;
\r
7639 ResetEvent(ovl->hEvent);
\r
7640 ovl->Offset = ovl->OffsetHigh = 0;
\r
7641 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7645 err = GetLastError();
\r
7646 if (err == ERROR_IO_PENDING) {
\r
7647 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7651 err = GetLastError();
\r
7658 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7663 ResetEvent(ovl->hEvent);
\r
7664 ovl->Offset = ovl->OffsetHigh = 0;
\r
7665 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7669 err = GetLastError();
\r
7670 if (err == ERROR_IO_PENDING) {
\r
7671 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7675 err = GetLastError();
\r
7682 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7683 void CheckForInputBufferFull( InputSource * is )
\r
7685 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7686 /* Look for end of line */
\r
7687 char * p = is->buf;
\r
7689 while( p < is->next && *p != '\n' ) {
\r
7693 if( p >= is->next ) {
\r
7694 if (appData.debugMode) {
\r
7695 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7698 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7699 is->count = (DWORD) -1;
\r
7700 is->next = is->buf;
\r
7706 InputThread(LPVOID arg)
\r
7711 is = (InputSource *) arg;
\r
7712 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7713 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7714 while (is->hThread != NULL) {
\r
7715 is->error = DoReadFile(is->hFile, is->next,
\r
7716 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7717 &is->count, &ovl);
\r
7718 if (is->error == NO_ERROR) {
\r
7719 is->next += is->count;
\r
7721 if (is->error == ERROR_BROKEN_PIPE) {
\r
7722 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7725 is->count = (DWORD) -1;
\r
7726 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7731 CheckForInputBufferFull( is );
\r
7733 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7735 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7737 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7740 CloseHandle(ovl.hEvent);
\r
7741 CloseHandle(is->hFile);
\r
7743 if (appData.debugMode) {
\r
7744 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7751 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7753 NonOvlInputThread(LPVOID arg)
\r
7760 is = (InputSource *) arg;
\r
7761 while (is->hThread != NULL) {
\r
7762 is->error = ReadFile(is->hFile, is->next,
\r
7763 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7764 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7765 if (is->error == NO_ERROR) {
\r
7766 /* Change CRLF to LF */
\r
7767 if (is->next > is->buf) {
\r
7769 i = is->count + 1;
\r
7777 if (prev == '\r' && *p == '\n') {
\r
7789 if (is->error == ERROR_BROKEN_PIPE) {
\r
7790 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7793 is->count = (DWORD) -1;
\r
7797 CheckForInputBufferFull( is );
\r
7799 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7801 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7803 if (is->count < 0) break; /* Quit on error */
\r
7805 CloseHandle(is->hFile);
\r
7810 SocketInputThread(LPVOID arg)
\r
7814 is = (InputSource *) arg;
\r
7815 while (is->hThread != NULL) {
\r
7816 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7817 if ((int)is->count == SOCKET_ERROR) {
\r
7818 is->count = (DWORD) -1;
\r
7819 is->error = WSAGetLastError();
\r
7821 is->error = NO_ERROR;
\r
7822 is->next += is->count;
\r
7823 if (is->count == 0 && is->second == is) {
\r
7824 /* End of file on stderr; quit with no message */
\r
7828 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7830 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7832 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7838 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7842 is = (InputSource *) lParam;
\r
7843 if (is->lineByLine) {
\r
7844 /* Feed in lines one by one */
\r
7845 char *p = is->buf;
\r
7847 while (q < is->next) {
\r
7848 if (*q++ == '\n') {
\r
7849 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7854 /* Move any partial line to the start of the buffer */
\r
7856 while (p < is->next) {
\r
7861 if (is->error != NO_ERROR || is->count == 0) {
\r
7862 /* Notify backend of the error. Note: If there was a partial
\r
7863 line at the end, it is not flushed through. */
\r
7864 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7867 /* Feed in the whole chunk of input at once */
\r
7868 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7869 is->next = is->buf;
\r
7873 /*---------------------------------------------------------------------------*\
\r
7875 * Menu enables. Used when setting various modes.
\r
7877 \*---------------------------------------------------------------------------*/
\r
7885 GreyRevert(Boolean grey)
\r
7886 { // [HGM] vari: for retracting variations in local mode
\r
7887 HMENU hmenu = GetMenu(hwndMain);
\r
7888 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7889 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7893 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7895 while (enab->item > 0) {
\r
7896 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7901 Enables gnuEnables[] = {
\r
7902 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7903 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7904 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7905 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7906 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7907 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7908 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7909 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7910 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7911 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7912 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7913 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7914 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7916 // Needed to switch from ncp to GNU mode on Engine Load
\r
7917 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7918 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7919 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7920 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7921 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7922 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7923 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
7924 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7925 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
7926 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
7927 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7928 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7929 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7930 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7934 Enables icsEnables[] = {
\r
7935 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7936 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7937 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7938 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7939 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7940 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7941 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7942 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7943 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7944 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7945 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7946 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7947 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7948 { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },
\r
7949 { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },
\r
7950 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7951 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7952 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7953 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7954 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
7959 Enables zippyEnables[] = {
\r
7960 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7961 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7962 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7963 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7968 Enables ncpEnables[] = {
\r
7969 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7970 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7971 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7972 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7973 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7974 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7975 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7976 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7977 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7978 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7979 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7980 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7981 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7982 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7983 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7984 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7985 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7986 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7987 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7988 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7989 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7990 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
7994 Enables trainingOnEnables[] = {
\r
7995 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7996 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
7997 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7998 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7999 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
8000 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
8001 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
8002 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8003 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
8007 Enables trainingOffEnables[] = {
\r
8008 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
8009 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
8010 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
8011 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
8012 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
8013 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
8014 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
8015 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8016 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
8020 /* These modify either ncpEnables or gnuEnables */
\r
8021 Enables cmailEnables[] = {
\r
8022 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
8023 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
8024 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
8025 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
8026 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
8027 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
8028 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
8032 Enables machineThinkingEnables[] = {
\r
8033 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8034 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
8035 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
8036 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8037 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
8038 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8039 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8040 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8041 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8042 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
8043 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8044 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8045 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8046 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8047 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
8048 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
8052 Enables userThinkingEnables[] = {
\r
8053 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8054 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
8055 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
8056 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8057 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
8058 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8059 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8060 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8061 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8062 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
8063 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
8064 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
8065 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
8066 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
8067 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
8068 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
8072 /*---------------------------------------------------------------------------*\
\r
8074 * Front-end interface functions exported by XBoard.
\r
8075 * Functions appear in same order as prototypes in frontend.h.
\r
8077 \*---------------------------------------------------------------------------*/
\r
8079 CheckMark(UINT item, int state)
\r
8081 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
8087 static UINT prevChecked = 0;
\r
8088 static int prevPausing = 0;
\r
8091 if (pausing != prevPausing) {
\r
8092 prevPausing = pausing;
\r
8093 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
8094 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
8095 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
8098 switch (gameMode) {
\r
8099 case BeginningOfGame:
\r
8100 if (appData.icsActive)
\r
8101 nowChecked = IDM_IcsClient;
\r
8102 else if (appData.noChessProgram)
\r
8103 nowChecked = IDM_EditGame;
\r
8105 nowChecked = IDM_MachineBlack;
\r
8107 case MachinePlaysBlack:
\r
8108 nowChecked = IDM_MachineBlack;
\r
8110 case MachinePlaysWhite:
\r
8111 nowChecked = IDM_MachineWhite;
\r
8113 case TwoMachinesPlay:
\r
8114 nowChecked = IDM_TwoMachines;
\r
8117 nowChecked = IDM_AnalysisMode;
\r
8120 nowChecked = IDM_AnalyzeFile;
\r
8123 nowChecked = IDM_EditGame;
\r
8125 case PlayFromGameFile:
\r
8126 nowChecked = IDM_LoadGame;
\r
8128 case EditPosition:
\r
8129 nowChecked = IDM_EditPosition;
\r
8132 nowChecked = IDM_Training;
\r
8134 case IcsPlayingWhite:
\r
8135 case IcsPlayingBlack:
\r
8136 case IcsObserving:
\r
8138 nowChecked = IDM_IcsClient;
\r
8145 CheckMark(prevChecked, MF_UNCHECKED);
\r
8146 CheckMark(nowChecked, MF_CHECKED);
\r
8147 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8149 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8150 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8151 MF_BYCOMMAND|MF_ENABLED);
\r
8153 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8154 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8157 prevChecked = nowChecked;
\r
8159 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8160 if (appData.icsActive) {
\r
8161 if (appData.icsEngineAnalyze) {
\r
8162 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8164 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8167 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8173 HMENU hmenu = GetMenu(hwndMain);
\r
8174 SetMenuEnables(hmenu, icsEnables);
\r
8175 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8176 MF_BYCOMMAND|MF_ENABLED);
\r
8178 if (appData.zippyPlay) {
\r
8179 SetMenuEnables(hmenu, zippyEnables);
\r
8180 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8181 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8182 MF_BYCOMMAND|MF_ENABLED);
\r
8190 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8196 HMENU hmenu = GetMenu(hwndMain);
\r
8197 SetMenuEnables(hmenu, ncpEnables);
\r
8198 DrawMenuBar(hwndMain);
\r
8204 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8208 SetTrainingModeOn()
\r
8211 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8212 for (i = 0; i < N_BUTTONS; i++) {
\r
8213 if (buttonDesc[i].hwnd != NULL)
\r
8214 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8219 VOID SetTrainingModeOff()
\r
8222 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8223 for (i = 0; i < N_BUTTONS; i++) {
\r
8224 if (buttonDesc[i].hwnd != NULL)
\r
8225 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8231 SetUserThinkingEnables()
\r
8233 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8237 SetMachineThinkingEnables()
\r
8239 HMENU hMenu = GetMenu(hwndMain);
\r
8240 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8242 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8244 if (gameMode == MachinePlaysBlack) {
\r
8245 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8246 } else if (gameMode == MachinePlaysWhite) {
\r
8247 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8248 } else if (gameMode == TwoMachinesPlay) {
\r
8249 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8255 DisplayTitle(char *str)
\r
8257 char title[MSG_SIZ], *host;
\r
8258 if (str[0] != NULLCHAR) {
\r
8259 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8260 } else if (appData.icsActive) {
\r
8261 if (appData.icsCommPort[0] != NULLCHAR)
\r
8264 host = appData.icsHost;
\r
8265 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8266 } else if (appData.noChessProgram) {
\r
8267 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8269 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8270 strcat(title, ": ");
\r
8271 strcat(title, first.tidy);
\r
8273 SetWindowText(hwndMain, title);
\r
8278 DisplayMessage(char *str1, char *str2)
\r
8282 int remain = MESSAGE_TEXT_MAX - 1;
\r
8285 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8286 messageText[0] = NULLCHAR;
\r
8288 len = strlen(str1);
\r
8289 if (len > remain) len = remain;
\r
8290 strncpy(messageText, str1, len);
\r
8291 messageText[len] = NULLCHAR;
\r
8294 if (*str2 && remain >= 2) {
\r
8296 strcat(messageText, " ");
\r
8299 len = strlen(str2);
\r
8300 if (len > remain) len = remain;
\r
8301 strncat(messageText, str2, len);
\r
8303 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8304 safeStrCpy(lastMsg, messageText, MSG_SIZ);
\r
8306 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8310 hdc = GetDC(hwndMain);
\r
8311 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8312 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8313 &messageRect, messageText, strlen(messageText), NULL);
\r
8314 (void) SelectObject(hdc, oldFont);
\r
8315 (void) ReleaseDC(hwndMain, hdc);
\r
8319 DisplayError(char *str, int error)
\r
8321 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8325 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8327 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8328 NULL, error, LANG_NEUTRAL,
\r
8329 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8331 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8333 ErrorMap *em = errmap;
\r
8334 while (em->err != 0 && em->err != error) em++;
\r
8335 if (em->err != 0) {
\r
8336 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8338 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8343 ErrorPopUp(_("Error"), buf);
\r
8348 DisplayMoveError(char *str)
\r
8350 fromX = fromY = -1;
\r
8351 ClearHighlights();
\r
8352 DrawPosition(FALSE, NULL);
\r
8353 if (appData.popupMoveErrors) {
\r
8354 ErrorPopUp(_("Error"), str);
\r
8356 DisplayMessage(str, "");
\r
8357 moveErrorMessageUp = TRUE;
\r
8362 DisplayFatalError(char *str, int error, int exitStatus)
\r
8364 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8366 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8369 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8370 NULL, error, LANG_NEUTRAL,
\r
8371 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8373 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8375 ErrorMap *em = errmap;
\r
8376 while (em->err != 0 && em->err != error) em++;
\r
8377 if (em->err != 0) {
\r
8378 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8380 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8385 if (appData.debugMode) {
\r
8386 fprintf(debugFP, "%s: %s\n", label, str);
\r
8388 if (appData.popupExitMessage) {
\r
8389 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8390 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8392 ExitEvent(exitStatus);
\r
8397 DisplayInformation(char *str)
\r
8399 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8404 DisplayNote(char *str)
\r
8406 ErrorPopUp(_("Note"), str);
\r
8411 char *title, *question, *replyPrefix;
\r
8416 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8418 static QuestionParams *qp;
\r
8419 char reply[MSG_SIZ];
\r
8422 switch (message) {
\r
8423 case WM_INITDIALOG:
\r
8424 qp = (QuestionParams *) lParam;
\r
8425 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8426 Translate(hDlg, DLG_Question);
\r
8427 SetWindowText(hDlg, qp->title);
\r
8428 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8429 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8433 switch (LOWORD(wParam)) {
\r
8435 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8436 if (*reply) strcat(reply, " ");
\r
8437 len = strlen(reply);
\r
8438 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8439 strcat(reply, "\n");
\r
8440 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8441 EndDialog(hDlg, TRUE);
\r
8442 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8445 EndDialog(hDlg, FALSE);
\r
8456 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8458 QuestionParams qp;
\r
8462 qp.question = question;
\r
8463 qp.replyPrefix = replyPrefix;
\r
8465 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8466 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8467 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8468 FreeProcInstance(lpProc);
\r
8471 /* [AS] Pick FRC position */
\r
8472 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8474 static int * lpIndexFRC;
\r
8480 case WM_INITDIALOG:
\r
8481 lpIndexFRC = (int *) lParam;
\r
8483 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8484 Translate(hDlg, DLG_NewGameFRC);
\r
8486 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8487 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8488 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8489 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8494 switch( LOWORD(wParam) ) {
\r
8496 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8497 EndDialog( hDlg, 0 );
\r
8498 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8501 EndDialog( hDlg, 1 );
\r
8503 case IDC_NFG_Edit:
\r
8504 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8505 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8507 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8510 case IDC_NFG_Random:
\r
8511 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8512 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8525 int index = appData.defaultFrcPosition;
\r
8526 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8528 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8530 if( result == 0 ) {
\r
8531 appData.defaultFrcPosition = index;
\r
8537 /* [AS] Game list options. Refactored by HGM */
\r
8539 HWND gameListOptionsDialog;
\r
8541 // low-level front-end: clear text edit / list widget
\r
8546 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8549 // low-level front-end: clear text edit / list widget
\r
8551 GLT_DeSelectList()
\r
8553 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8556 // low-level front-end: append line to text edit / list widget
\r
8558 GLT_AddToList( char *name )
\r
8561 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8565 // low-level front-end: get line from text edit / list widget
\r
8567 GLT_GetFromList( int index, char *name )
\r
8570 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8576 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8578 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8579 int idx2 = idx1 + delta;
\r
8580 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8582 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8585 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8586 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8587 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8588 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8592 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8596 case WM_INITDIALOG:
\r
8597 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8599 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8600 Translate(hDlg, DLG_GameListOptions);
\r
8602 /* Initialize list */
\r
8603 GLT_TagsToList( lpUserGLT );
\r
8605 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8610 switch( LOWORD(wParam) ) {
\r
8613 EndDialog( hDlg, 0 );
\r
8616 EndDialog( hDlg, 1 );
\r
8619 case IDC_GLT_Default:
\r
8620 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8623 case IDC_GLT_Restore:
\r
8624 GLT_TagsToList( appData.gameListTags );
\r
8628 GLT_MoveSelection( hDlg, -1 );
\r
8631 case IDC_GLT_Down:
\r
8632 GLT_MoveSelection( hDlg, +1 );
\r
8642 int GameListOptions()
\r
8645 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8647 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8649 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8651 if( result == 0 ) {
\r
8652 /* [AS] Memory leak here! */
\r
8653 appData.gameListTags = strdup( lpUserGLT );
\r
8660 DisplayIcsInteractionTitle(char *str)
\r
8662 char consoleTitle[MSG_SIZ];
\r
8664 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8665 SetWindowText(hwndConsole, consoleTitle);
\r
8667 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
8668 char buf[MSG_SIZ], *p = buf, *q;
\r
8669 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
8671 q = strchr(p, ';');
\r
8673 if(*p) ChatPopUp(p);
\r
8677 SetActiveWindow(hwndMain);
\r
8681 DrawPosition(int fullRedraw, Board board)
\r
8683 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8686 void NotifyFrontendLogin()
\r
8689 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8695 fromX = fromY = -1;
\r
8696 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8697 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8698 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8699 dragInfo.lastpos = dragInfo.pos;
\r
8700 dragInfo.start.x = dragInfo.start.y = -1;
\r
8701 dragInfo.from = dragInfo.start;
\r
8703 DrawPosition(TRUE, NULL);
\r
8710 CommentPopUp(char *title, char *str)
\r
8712 HWND hwnd = GetActiveWindow();
\r
8713 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8715 SetActiveWindow(hwnd);
\r
8719 CommentPopDown(void)
\r
8721 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8722 if (commentDialog) {
\r
8723 ShowWindow(commentDialog, SW_HIDE);
\r
8725 commentUp = FALSE;
\r
8729 EditCommentPopUp(int index, char *title, char *str)
\r
8731 EitherCommentPopUp(index, title, str, TRUE);
\r
8738 MyPlaySound(&sounds[(int)SoundRoar]);
\r
8745 MyPlaySound(&sounds[(int)SoundMove]);
\r
8748 VOID PlayIcsWinSound()
\r
8750 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8753 VOID PlayIcsLossSound()
\r
8755 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8758 VOID PlayIcsDrawSound()
\r
8760 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8763 VOID PlayIcsUnfinishedSound()
\r
8765 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8771 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8777 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8785 consoleEcho = TRUE;
\r
8786 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8787 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8788 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8797 consoleEcho = FALSE;
\r
8798 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8799 /* This works OK: set text and background both to the same color */
\r
8801 cf.crTextColor = COLOR_ECHOOFF;
\r
8802 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8803 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8806 /* No Raw()...? */
\r
8808 void Colorize(ColorClass cc, int continuation)
\r
8810 currentColorClass = cc;
\r
8811 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8812 consoleCF.crTextColor = textAttribs[cc].color;
\r
8813 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8814 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8820 static char buf[MSG_SIZ];
\r
8821 DWORD bufsiz = MSG_SIZ;
\r
8823 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8824 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8826 if (!GetUserName(buf, &bufsiz)) {
\r
8827 /*DisplayError("Error getting user name", GetLastError());*/
\r
8828 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8836 static char buf[MSG_SIZ];
\r
8837 DWORD bufsiz = MSG_SIZ;
\r
8839 if (!GetComputerName(buf, &bufsiz)) {
\r
8840 /*DisplayError("Error getting host name", GetLastError());*/
\r
8841 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8848 ClockTimerRunning()
\r
8850 return clockTimerEvent != 0;
\r
8856 if (clockTimerEvent == 0) return FALSE;
\r
8857 KillTimer(hwndMain, clockTimerEvent);
\r
8858 clockTimerEvent = 0;
\r
8863 StartClockTimer(long millisec)
\r
8865 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8866 (UINT) millisec, NULL);
\r
8870 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8873 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8875 if(appData.noGUI) return;
\r
8876 hdc = GetDC(hwndMain);
\r
8877 if (!IsIconic(hwndMain)) {
\r
8878 DisplayAClock(hdc, timeRemaining, highlight,
\r
8879 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8881 if (highlight && iconCurrent == iconBlack) {
\r
8882 iconCurrent = iconWhite;
\r
8883 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8884 if (IsIconic(hwndMain)) {
\r
8885 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8888 (void) ReleaseDC(hwndMain, hdc);
\r
8890 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8894 DisplayBlackClock(long timeRemaining, int highlight)
\r
8897 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8900 if(appData.noGUI) return;
\r
8901 hdc = GetDC(hwndMain);
\r
8902 if (!IsIconic(hwndMain)) {
\r
8903 DisplayAClock(hdc, timeRemaining, highlight,
\r
8904 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8906 if (highlight && iconCurrent == iconWhite) {
\r
8907 iconCurrent = iconBlack;
\r
8908 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8909 if (IsIconic(hwndMain)) {
\r
8910 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8913 (void) ReleaseDC(hwndMain, hdc);
\r
8915 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8920 LoadGameTimerRunning()
\r
8922 return loadGameTimerEvent != 0;
\r
8926 StopLoadGameTimer()
\r
8928 if (loadGameTimerEvent == 0) return FALSE;
\r
8929 KillTimer(hwndMain, loadGameTimerEvent);
\r
8930 loadGameTimerEvent = 0;
\r
8935 StartLoadGameTimer(long millisec)
\r
8937 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8938 (UINT) millisec, NULL);
\r
8946 char fileTitle[MSG_SIZ];
\r
8948 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8949 f = OpenFileDialog(hwndMain, "a", defName,
\r
8950 appData.oldSaveStyle ? "gam" : "pgn",
\r
8952 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8954 SaveGame(f, 0, "");
\r
8961 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8963 if (delayedTimerEvent != 0) {
\r
8964 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8965 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8967 KillTimer(hwndMain, delayedTimerEvent);
\r
8968 delayedTimerEvent = 0;
\r
8969 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8970 delayedTimerCallback();
\r
8972 delayedTimerCallback = cb;
\r
8973 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8974 (UINT) millisec, NULL);
\r
8977 DelayedEventCallback
\r
8980 if (delayedTimerEvent) {
\r
8981 return delayedTimerCallback;
\r
8988 CancelDelayedEvent()
\r
8990 if (delayedTimerEvent) {
\r
8991 KillTimer(hwndMain, delayedTimerEvent);
\r
8992 delayedTimerEvent = 0;
\r
8996 DWORD GetWin32Priority(int nice)
\r
8997 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8999 REALTIME_PRIORITY_CLASS 0x00000100
\r
9000 HIGH_PRIORITY_CLASS 0x00000080
\r
9001 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
9002 NORMAL_PRIORITY_CLASS 0x00000020
\r
9003 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
9004 IDLE_PRIORITY_CLASS 0x00000040
\r
9006 if (nice < -15) return 0x00000080;
\r
9007 if (nice < 0) return 0x00008000;
\r
9008 if (nice == 0) return 0x00000020;
\r
9009 if (nice < 15) return 0x00004000;
\r
9010 return 0x00000040;
\r
9013 void RunCommand(char *cmdLine)
\r
9015 /* Now create the child process. */
\r
9016 STARTUPINFO siStartInfo;
\r
9017 PROCESS_INFORMATION piProcInfo;
\r
9019 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9020 siStartInfo.lpReserved = NULL;
\r
9021 siStartInfo.lpDesktop = NULL;
\r
9022 siStartInfo.lpTitle = NULL;
\r
9023 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9024 siStartInfo.cbReserved2 = 0;
\r
9025 siStartInfo.lpReserved2 = NULL;
\r
9026 siStartInfo.hStdInput = NULL;
\r
9027 siStartInfo.hStdOutput = NULL;
\r
9028 siStartInfo.hStdError = NULL;
\r
9030 CreateProcess(NULL,
\r
9031 cmdLine, /* command line */
\r
9032 NULL, /* process security attributes */
\r
9033 NULL, /* primary thread security attrs */
\r
9034 TRUE, /* handles are inherited */
\r
9035 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9036 NULL, /* use parent's environment */
\r
9038 &siStartInfo, /* STARTUPINFO pointer */
\r
9039 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9041 CloseHandle(piProcInfo.hThread);
\r
9044 /* Start a child process running the given program.
\r
9045 The process's standard output can be read from "from", and its
\r
9046 standard input can be written to "to".
\r
9047 Exit with fatal error if anything goes wrong.
\r
9048 Returns an opaque pointer that can be used to destroy the process
\r
9052 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
9054 #define BUFSIZE 4096
\r
9056 HANDLE hChildStdinRd, hChildStdinWr,
\r
9057 hChildStdoutRd, hChildStdoutWr;
\r
9058 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
9059 SECURITY_ATTRIBUTES saAttr;
\r
9061 PROCESS_INFORMATION piProcInfo;
\r
9062 STARTUPINFO siStartInfo;
\r
9064 char buf[MSG_SIZ];
\r
9067 if (appData.debugMode) {
\r
9068 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
9073 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
9074 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
9075 saAttr.bInheritHandle = TRUE;
\r
9076 saAttr.lpSecurityDescriptor = NULL;
\r
9079 * The steps for redirecting child's STDOUT:
\r
9080 * 1. Create anonymous pipe to be STDOUT for child.
\r
9081 * 2. Create a noninheritable duplicate of read handle,
\r
9082 * and close the inheritable read handle.
\r
9085 /* Create a pipe for the child's STDOUT. */
\r
9086 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
9087 return GetLastError();
\r
9090 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
9091 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
9092 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
9093 FALSE, /* not inherited */
\r
9094 DUPLICATE_SAME_ACCESS);
\r
9096 return GetLastError();
\r
9098 CloseHandle(hChildStdoutRd);
\r
9101 * The steps for redirecting child's STDIN:
\r
9102 * 1. Create anonymous pipe to be STDIN for child.
\r
9103 * 2. Create a noninheritable duplicate of write handle,
\r
9104 * and close the inheritable write handle.
\r
9107 /* Create a pipe for the child's STDIN. */
\r
9108 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
9109 return GetLastError();
\r
9112 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
9113 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
9114 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
9115 FALSE, /* not inherited */
\r
9116 DUPLICATE_SAME_ACCESS);
\r
9118 return GetLastError();
\r
9120 CloseHandle(hChildStdinWr);
\r
9122 /* Arrange to (1) look in dir for the child .exe file, and
\r
9123 * (2) have dir be the child's working directory. Interpret
\r
9124 * dir relative to the directory WinBoard loaded from. */
\r
9125 GetCurrentDirectory(MSG_SIZ, buf);
\r
9126 SetCurrentDirectory(installDir);
\r
9127 SetCurrentDirectory(dir);
\r
9129 /* Now create the child process. */
\r
9131 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9132 siStartInfo.lpReserved = NULL;
\r
9133 siStartInfo.lpDesktop = NULL;
\r
9134 siStartInfo.lpTitle = NULL;
\r
9135 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9136 siStartInfo.cbReserved2 = 0;
\r
9137 siStartInfo.lpReserved2 = NULL;
\r
9138 siStartInfo.hStdInput = hChildStdinRd;
\r
9139 siStartInfo.hStdOutput = hChildStdoutWr;
\r
9140 siStartInfo.hStdError = hChildStdoutWr;
\r
9142 fSuccess = CreateProcess(NULL,
\r
9143 cmdLine, /* command line */
\r
9144 NULL, /* process security attributes */
\r
9145 NULL, /* primary thread security attrs */
\r
9146 TRUE, /* handles are inherited */
\r
9147 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9148 NULL, /* use parent's environment */
\r
9150 &siStartInfo, /* STARTUPINFO pointer */
\r
9151 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9153 err = GetLastError();
\r
9154 SetCurrentDirectory(buf); /* return to prev directory */
\r
9159 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
9160 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
9161 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
9164 /* Close the handles we don't need in the parent */
\r
9165 CloseHandle(piProcInfo.hThread);
\r
9166 CloseHandle(hChildStdinRd);
\r
9167 CloseHandle(hChildStdoutWr);
\r
9169 /* Prepare return value */
\r
9170 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9171 cp->kind = CPReal;
\r
9172 cp->hProcess = piProcInfo.hProcess;
\r
9173 cp->pid = piProcInfo.dwProcessId;
\r
9174 cp->hFrom = hChildStdoutRdDup;
\r
9175 cp->hTo = hChildStdinWrDup;
\r
9177 *pr = (void *) cp;
\r
9179 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
9180 2000 where engines sometimes don't see the initial command(s)
\r
9181 from WinBoard and hang. I don't understand how that can happen,
\r
9182 but the Sleep is harmless, so I've put it in. Others have also
\r
9183 reported what may be the same problem, so hopefully this will fix
\r
9184 it for them too. */
\r
9192 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
9194 ChildProc *cp; int result;
\r
9196 cp = (ChildProc *) pr;
\r
9197 if (cp == NULL) return;
\r
9199 switch (cp->kind) {
\r
9201 /* TerminateProcess is considered harmful, so... */
\r
9202 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9203 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9204 /* The following doesn't work because the chess program
\r
9205 doesn't "have the same console" as WinBoard. Maybe
\r
9206 we could arrange for this even though neither WinBoard
\r
9207 nor the chess program uses a console for stdio? */
\r
9208 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9210 /* [AS] Special termination modes for misbehaving programs... */
\r
9211 if( signal == 9 ) {
\r
9212 result = TerminateProcess( cp->hProcess, 0 );
\r
9214 if ( appData.debugMode) {
\r
9215 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9218 else if( signal == 10 ) {
\r
9219 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
9221 if( dw != WAIT_OBJECT_0 ) {
\r
9222 result = TerminateProcess( cp->hProcess, 0 );
\r
9224 if ( appData.debugMode) {
\r
9225 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9231 CloseHandle(cp->hProcess);
\r
9235 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9239 closesocket(cp->sock);
\r
9244 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9245 closesocket(cp->sock);
\r
9246 closesocket(cp->sock2);
\r
9254 InterruptChildProcess(ProcRef pr)
\r
9258 cp = (ChildProc *) pr;
\r
9259 if (cp == NULL) return;
\r
9260 switch (cp->kind) {
\r
9262 /* The following doesn't work because the chess program
\r
9263 doesn't "have the same console" as WinBoard. Maybe
\r
9264 we could arrange for this even though neither WinBoard
\r
9265 nor the chess program uses a console for stdio */
\r
9266 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9271 /* Can't interrupt */
\r
9275 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9282 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9284 char cmdLine[MSG_SIZ];
\r
9286 if (port[0] == NULLCHAR) {
\r
9287 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9289 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9291 return StartChildProcess(cmdLine, "", pr);
\r
9295 /* Code to open TCP sockets */
\r
9298 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9304 struct sockaddr_in sa, mysa;
\r
9305 struct hostent FAR *hp;
\r
9306 unsigned short uport;
\r
9307 WORD wVersionRequested;
\r
9310 /* Initialize socket DLL */
\r
9311 wVersionRequested = MAKEWORD(1, 1);
\r
9312 err = WSAStartup(wVersionRequested, &wsaData);
\r
9313 if (err != 0) return err;
\r
9316 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9317 err = WSAGetLastError();
\r
9322 /* Bind local address using (mostly) don't-care values.
\r
9324 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9325 mysa.sin_family = AF_INET;
\r
9326 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9327 uport = (unsigned short) 0;
\r
9328 mysa.sin_port = htons(uport);
\r
9329 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9330 == SOCKET_ERROR) {
\r
9331 err = WSAGetLastError();
\r
9336 /* Resolve remote host name */
\r
9337 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9338 if (!(hp = gethostbyname(host))) {
\r
9339 unsigned int b0, b1, b2, b3;
\r
9341 err = WSAGetLastError();
\r
9343 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9344 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9345 hp->h_addrtype = AF_INET;
\r
9347 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9348 hp->h_addr_list[0] = (char *) malloc(4);
\r
9349 hp->h_addr_list[0][0] = (char) b0;
\r
9350 hp->h_addr_list[0][1] = (char) b1;
\r
9351 hp->h_addr_list[0][2] = (char) b2;
\r
9352 hp->h_addr_list[0][3] = (char) b3;
\r
9358 sa.sin_family = hp->h_addrtype;
\r
9359 uport = (unsigned short) atoi(port);
\r
9360 sa.sin_port = htons(uport);
\r
9361 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9363 /* Make connection */
\r
9364 if (connect(s, (struct sockaddr *) &sa,
\r
9365 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9366 err = WSAGetLastError();
\r
9371 /* Prepare return value */
\r
9372 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9373 cp->kind = CPSock;
\r
9375 *pr = (ProcRef *) cp;
\r
9381 OpenCommPort(char *name, ProcRef *pr)
\r
9386 char fullname[MSG_SIZ];
\r
9388 if (*name != '\\')
\r
9389 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9391 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9393 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9394 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9395 if (h == (HANDLE) -1) {
\r
9396 return GetLastError();
\r
9400 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9402 /* Accumulate characters until a 100ms pause, then parse */
\r
9403 ct.ReadIntervalTimeout = 100;
\r
9404 ct.ReadTotalTimeoutMultiplier = 0;
\r
9405 ct.ReadTotalTimeoutConstant = 0;
\r
9406 ct.WriteTotalTimeoutMultiplier = 0;
\r
9407 ct.WriteTotalTimeoutConstant = 0;
\r
9408 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9410 /* Prepare return value */
\r
9411 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9412 cp->kind = CPComm;
\r
9415 *pr = (ProcRef *) cp;
\r
9421 OpenLoopback(ProcRef *pr)
\r
9423 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9429 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9434 struct sockaddr_in sa, mysa;
\r
9435 struct hostent FAR *hp;
\r
9436 unsigned short uport;
\r
9437 WORD wVersionRequested;
\r
9440 char stderrPortStr[MSG_SIZ];
\r
9442 /* Initialize socket DLL */
\r
9443 wVersionRequested = MAKEWORD(1, 1);
\r
9444 err = WSAStartup(wVersionRequested, &wsaData);
\r
9445 if (err != 0) return err;
\r
9447 /* Resolve remote host name */
\r
9448 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9449 if (!(hp = gethostbyname(host))) {
\r
9450 unsigned int b0, b1, b2, b3;
\r
9452 err = WSAGetLastError();
\r
9454 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9455 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9456 hp->h_addrtype = AF_INET;
\r
9458 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9459 hp->h_addr_list[0] = (char *) malloc(4);
\r
9460 hp->h_addr_list[0][0] = (char) b0;
\r
9461 hp->h_addr_list[0][1] = (char) b1;
\r
9462 hp->h_addr_list[0][2] = (char) b2;
\r
9463 hp->h_addr_list[0][3] = (char) b3;
\r
9469 sa.sin_family = hp->h_addrtype;
\r
9470 uport = (unsigned short) 514;
\r
9471 sa.sin_port = htons(uport);
\r
9472 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9474 /* Bind local socket to unused "privileged" port address
\r
9476 s = INVALID_SOCKET;
\r
9477 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9478 mysa.sin_family = AF_INET;
\r
9479 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9480 for (fromPort = 1023;; fromPort--) {
\r
9481 if (fromPort < 0) {
\r
9483 return WSAEADDRINUSE;
\r
9485 if (s == INVALID_SOCKET) {
\r
9486 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9487 err = WSAGetLastError();
\r
9492 uport = (unsigned short) fromPort;
\r
9493 mysa.sin_port = htons(uport);
\r
9494 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9495 == SOCKET_ERROR) {
\r
9496 err = WSAGetLastError();
\r
9497 if (err == WSAEADDRINUSE) continue;
\r
9501 if (connect(s, (struct sockaddr *) &sa,
\r
9502 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9503 err = WSAGetLastError();
\r
9504 if (err == WSAEADDRINUSE) {
\r
9515 /* Bind stderr local socket to unused "privileged" port address
\r
9517 s2 = INVALID_SOCKET;
\r
9518 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9519 mysa.sin_family = AF_INET;
\r
9520 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9521 for (fromPort = 1023;; fromPort--) {
\r
9522 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9523 if (fromPort < 0) {
\r
9524 (void) closesocket(s);
\r
9526 return WSAEADDRINUSE;
\r
9528 if (s2 == INVALID_SOCKET) {
\r
9529 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9530 err = WSAGetLastError();
\r
9536 uport = (unsigned short) fromPort;
\r
9537 mysa.sin_port = htons(uport);
\r
9538 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9539 == SOCKET_ERROR) {
\r
9540 err = WSAGetLastError();
\r
9541 if (err == WSAEADDRINUSE) continue;
\r
9542 (void) closesocket(s);
\r
9546 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9547 err = WSAGetLastError();
\r
9548 if (err == WSAEADDRINUSE) {
\r
9550 s2 = INVALID_SOCKET;
\r
9553 (void) closesocket(s);
\r
9554 (void) closesocket(s2);
\r
9560 prevStderrPort = fromPort; // remember port used
\r
9561 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9563 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9564 err = WSAGetLastError();
\r
9565 (void) closesocket(s);
\r
9566 (void) closesocket(s2);
\r
9571 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9572 err = WSAGetLastError();
\r
9573 (void) closesocket(s);
\r
9574 (void) closesocket(s2);
\r
9578 if (*user == NULLCHAR) user = UserName();
\r
9579 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9580 err = WSAGetLastError();
\r
9581 (void) closesocket(s);
\r
9582 (void) closesocket(s2);
\r
9586 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9587 err = WSAGetLastError();
\r
9588 (void) closesocket(s);
\r
9589 (void) closesocket(s2);
\r
9594 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9595 err = WSAGetLastError();
\r
9596 (void) closesocket(s);
\r
9597 (void) closesocket(s2);
\r
9601 (void) closesocket(s2); /* Stop listening */
\r
9603 /* Prepare return value */
\r
9604 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9605 cp->kind = CPRcmd;
\r
9608 *pr = (ProcRef *) cp;
\r
9615 AddInputSource(ProcRef pr, int lineByLine,
\r
9616 InputCallback func, VOIDSTAR closure)
\r
9618 InputSource *is, *is2 = NULL;
\r
9619 ChildProc *cp = (ChildProc *) pr;
\r
9621 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9622 is->lineByLine = lineByLine;
\r
9624 is->closure = closure;
\r
9625 is->second = NULL;
\r
9626 is->next = is->buf;
\r
9627 if (pr == NoProc) {
\r
9628 is->kind = CPReal;
\r
9629 consoleInputSource = is;
\r
9631 is->kind = cp->kind;
\r
9633 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9634 we create all threads suspended so that the is->hThread variable can be
\r
9635 safely assigned, then let the threads start with ResumeThread.
\r
9637 switch (cp->kind) {
\r
9639 is->hFile = cp->hFrom;
\r
9640 cp->hFrom = NULL; /* now owned by InputThread */
\r
9642 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9643 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9647 is->hFile = cp->hFrom;
\r
9648 cp->hFrom = NULL; /* now owned by InputThread */
\r
9650 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9651 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9655 is->sock = cp->sock;
\r
9657 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9658 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9662 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9664 is->sock = cp->sock;
\r
9666 is2->sock = cp->sock2;
\r
9667 is2->second = is2;
\r
9669 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9670 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9672 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9673 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9677 if( is->hThread != NULL ) {
\r
9678 ResumeThread( is->hThread );
\r
9681 if( is2 != NULL && is2->hThread != NULL ) {
\r
9682 ResumeThread( is2->hThread );
\r
9686 return (InputSourceRef) is;
\r
9690 RemoveInputSource(InputSourceRef isr)
\r
9694 is = (InputSource *) isr;
\r
9695 is->hThread = NULL; /* tell thread to stop */
\r
9696 CloseHandle(is->hThread);
\r
9697 if (is->second != NULL) {
\r
9698 is->second->hThread = NULL;
\r
9699 CloseHandle(is->second->hThread);
\r
9703 int no_wrap(char *message, int count)
\r
9705 ConsoleOutput(message, count, FALSE);
\r
9710 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9713 int outCount = SOCKET_ERROR;
\r
9714 ChildProc *cp = (ChildProc *) pr;
\r
9715 static OVERLAPPED ovl;
\r
9716 static int line = 0;
\r
9720 if (appData.noJoin || !appData.useInternalWrap)
\r
9721 return no_wrap(message, count);
\r
9724 int width = get_term_width();
\r
9725 int len = wrap(NULL, message, count, width, &line);
\r
9726 char *msg = malloc(len);
\r
9730 return no_wrap(message, count);
\r
9733 dbgchk = wrap(msg, message, count, width, &line);
\r
9734 if (dbgchk != len && appData.debugMode)
\r
9735 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9736 ConsoleOutput(msg, len, FALSE);
\r
9743 if (ovl.hEvent == NULL) {
\r
9744 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9746 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9748 switch (cp->kind) {
\r
9751 outCount = send(cp->sock, message, count, 0);
\r
9752 if (outCount == SOCKET_ERROR) {
\r
9753 *outError = WSAGetLastError();
\r
9755 *outError = NO_ERROR;
\r
9760 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9761 &dOutCount, NULL)) {
\r
9762 *outError = NO_ERROR;
\r
9763 outCount = (int) dOutCount;
\r
9765 *outError = GetLastError();
\r
9770 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9771 &dOutCount, &ovl);
\r
9772 if (*outError == NO_ERROR) {
\r
9773 outCount = (int) dOutCount;
\r
9783 if(n != 0) Sleep(n);
\r
9787 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9790 /* Ignore delay, not implemented for WinBoard */
\r
9791 return OutputToProcess(pr, message, count, outError);
\r
9796 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9797 char *buf, int count, int error)
\r
9799 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9802 /* see wgamelist.c for Game List functions */
\r
9803 /* see wedittags.c for Edit Tags functions */
\r
9810 char buf[MSG_SIZ];
\r
9813 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9814 f = fopen(buf, "r");
\r
9816 ProcessICSInitScript(f);
\r
9826 StartAnalysisClock()
\r
9828 if (analysisTimerEvent) return;
\r
9829 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9830 (UINT) 2000, NULL);
\r
9834 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9836 highlightInfo.sq[0].x = fromX;
\r
9837 highlightInfo.sq[0].y = fromY;
\r
9838 highlightInfo.sq[1].x = toX;
\r
9839 highlightInfo.sq[1].y = toY;
\r
9845 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9846 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9850 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9852 premoveHighlightInfo.sq[0].x = fromX;
\r
9853 premoveHighlightInfo.sq[0].y = fromY;
\r
9854 premoveHighlightInfo.sq[1].x = toX;
\r
9855 premoveHighlightInfo.sq[1].y = toY;
\r
9859 ClearPremoveHighlights()
\r
9861 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9862 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9866 ShutDownFrontEnd()
\r
9868 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9869 DeleteClipboardTempFiles();
\r
9875 if (IsIconic(hwndMain))
\r
9876 ShowWindow(hwndMain, SW_RESTORE);
\r
9878 SetActiveWindow(hwndMain);
\r
9882 * Prototypes for animation support routines
\r
9884 static void ScreenSquare(int column, int row, POINT * pt);
\r
9885 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9886 POINT frames[], int * nFrames);
\r
9892 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9893 { // [HGM] atomic: animate blast wave
\r
9896 explodeInfo.fromX = fromX;
\r
9897 explodeInfo.fromY = fromY;
\r
9898 explodeInfo.toX = toX;
\r
9899 explodeInfo.toY = toY;
\r
9900 for(i=1; i<4*kFactor; i++) {
\r
9901 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9902 DrawPosition(FALSE, board);
\r
9903 Sleep(appData.animSpeed);
\r
9905 explodeInfo.radius = 0;
\r
9906 DrawPosition(TRUE, board);
\r
9910 AnimateMove(board, fromX, fromY, toX, toY)
\r
9917 ChessSquare piece;
\r
9918 int x = toX, y = toY;
\r
9919 POINT start, finish, mid;
\r
9920 POINT frames[kFactor * 2 + 1];
\r
9923 if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();
\r
9925 if (!appData.animate) return;
\r
9926 if (doingSizing) return;
\r
9927 if (fromY < 0 || fromX < 0) return;
\r
9928 piece = board[fromY][fromX];
\r
9929 if (piece >= EmptySquare) return;
\r
9931 if(killX >= 0) toX = killX, toY = killY; // [HGM] lion: first to kill square
\r
9935 ScreenSquare(fromX, fromY, &start);
\r
9936 ScreenSquare(toX, toY, &finish);
\r
9938 /* All moves except knight jumps move in straight line */
\r
9939 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9940 mid.x = start.x + (finish.x - start.x) / 2;
\r
9941 mid.y = start.y + (finish.y - start.y) / 2;
\r
9943 /* Knight: make straight movement then diagonal */
\r
9944 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9945 mid.x = start.x + (finish.x - start.x) / 2;
\r
9949 mid.y = start.y + (finish.y - start.y) / 2;
\r
9953 /* Don't use as many frames for very short moves */
\r
9954 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9955 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9957 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9959 animInfo.from.x = fromX;
\r
9960 animInfo.from.y = fromY;
\r
9961 animInfo.to.x = toX;
\r
9962 animInfo.to.y = toY;
\r
9963 animInfo.lastpos = start;
\r
9964 animInfo.piece = piece;
\r
9965 for (n = 0; n < nFrames; n++) {
\r
9966 animInfo.pos = frames[n];
\r
9967 DrawPosition(FALSE, NULL);
\r
9968 animInfo.lastpos = animInfo.pos;
\r
9969 Sleep(appData.animSpeed);
\r
9971 animInfo.pos = finish;
\r
9972 DrawPosition(FALSE, NULL);
\r
9974 if(toX != x || toY != y) { fromX = toX; fromY = toY; toX = x; toY = y; goto again; } // second leg
\r
9976 animInfo.piece = EmptySquare;
\r
9977 Explode(board, fromX, fromY, toX, toY);
\r
9980 /* Convert board position to corner of screen rect and color */
\r
9983 ScreenSquare(column, row, pt)
\r
9984 int column; int row; POINT * pt;
\r
9987 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
9988 pt->y = lineGap + row * (squareSize + lineGap) + border;
\r
9990 pt->x = lineGap + column * (squareSize + lineGap) + border;
\r
9991 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
9995 /* Generate a series of frame coords from start->mid->finish.
\r
9996 The movement rate doubles until the half way point is
\r
9997 reached, then halves back down to the final destination,
\r
9998 which gives a nice slow in/out effect. The algorithmn
\r
9999 may seem to generate too many intermediates for short
\r
10000 moves, but remember that the purpose is to attract the
\r
10001 viewers attention to the piece about to be moved and
\r
10002 then to where it ends up. Too few frames would be less
\r
10006 Tween(start, mid, finish, factor, frames, nFrames)
\r
10007 POINT * start; POINT * mid;
\r
10008 POINT * finish; int factor;
\r
10009 POINT frames[]; int * nFrames;
\r
10011 int n, fraction = 1, count = 0;
\r
10013 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
10014 for (n = 0; n < factor; n++)
\r
10016 for (n = 0; n < factor; n++) {
\r
10017 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
10018 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
10020 fraction = fraction / 2;
\r
10024 frames[count] = *mid;
\r
10027 /* Slow out, stepping 1/2, then 1/4, ... */
\r
10029 for (n = 0; n < factor; n++) {
\r
10030 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
10031 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
10033 fraction = fraction * 2;
\r
10035 *nFrames = count;
\r
10039 SettingsPopUp(ChessProgramState *cps)
\r
10040 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
10041 EngineOptionsPopup(savedHwnd, cps);
\r
10044 int flock(int fid, int code)
\r
10046 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
10048 ov.hEvent = NULL;
\r
10050 ov.OffsetHigh = 0;
\r
10052 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
10053 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
10054 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
10055 default: return -1;
\r
10064 static char col[8][20];
\r
10065 COLORREF color = *(COLORREF *) colorVariable[n];
\r
10067 snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
10072 ActivateTheme (int new)
\r
10073 { // Redo initialization of features depending on options that can occur in themes
\r
10075 if(new) InitDrawingColors();
\r
10076 fontBitmapSquareSize = 0; // request creation of new font pieces
\r
10077 InitDrawingSizes(boardSize, 0);
\r
10078 InvalidateRect(hwndMain, NULL, TRUE);
\r