2 * WinBoard.c -- Windows NT front end to XBoard
\r
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
\r
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
\r
8 * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc.
\r
10 * Enhancements Copyright 2005 Alessandro Scotti
\r
12 * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,
\r
13 * which was written and is copyrighted by Wayne Christopher.
\r
15 * The following terms apply to Digital Equipment Corporation's copyright
\r
16 * interest in XBoard:
\r
17 * ------------------------------------------------------------------------
\r
18 * All Rights Reserved
\r
20 * Permission to use, copy, modify, and distribute this software and its
\r
21 * documentation for any purpose and without fee is hereby granted,
\r
22 * provided that the above copyright notice appear in all copies and that
\r
23 * both that copyright notice and this permission notice appear in
\r
24 * supporting documentation, and that the name of Digital not be
\r
25 * used in advertising or publicity pertaining to distribution of the
\r
26 * software without specific, written prior permission.
\r
28 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
\r
29 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
\r
30 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
\r
31 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
\r
32 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
\r
33 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
\r
35 * ------------------------------------------------------------------------
\r
37 * The following terms apply to the enhanced version of XBoard
\r
38 * distributed by the Free Software Foundation:
\r
39 * ------------------------------------------------------------------------
\r
41 * GNU XBoard is free software: you can redistribute it and/or modify
\r
42 * it under the terms of the GNU General Public License as published by
\r
43 * the Free Software Foundation, either version 3 of the License, or (at
\r
44 * your option) any later version.
\r
46 * GNU XBoard is distributed in the hope that it will be useful, but
\r
47 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
48 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
49 * General Public License for more details.
\r
51 * You should have received a copy of the GNU General Public License
\r
52 * along with this program. If not, see http://www.gnu.org/licenses/. *
\r
54 *------------------------------------------------------------------------
\r
55 ** See the file ChangeLog for a revision history. */
\r
59 #include <windows.h>
\r
60 #include <winuser.h>
\r
61 #include <winsock.h>
\r
62 #include <commctrl.h>
\r
68 #include <sys/stat.h>
\r
71 #include <commdlg.h>
\r
73 #include <richedit.h>
\r
74 #include <mmsystem.h>
\r
84 #include "frontend.h"
\r
85 #include "backend.h"
\r
86 #include "winboard.h"
\r
88 #include "wclipbrd.h"
\r
89 #include "woptions.h"
\r
90 #include "wsockerr.h"
\r
91 #include "defaults.h"
\r
96 #define DATADIR "~~"
\r
98 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
\r
100 int myrandom(void);
\r
101 void mysrandom(unsigned int seed);
\r
103 extern int whiteFlag, blackFlag;
\r
104 Boolean flipClock = FALSE;
\r
105 extern HANDLE chatHandle[];
\r
106 extern enum ICS_TYPE ics_type;
\r
108 int MySearchPath P((char *installDir, char *name, char *fullname));
\r
109 int MyGetFullPathName P((char *name, char *fullname));
\r
110 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
111 VOID NewVariantPopup(HWND hwnd);
\r
112 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
113 /*char*/int promoChar));
\r
114 void DisplayMove P((int moveNumber));
\r
115 void ChatPopUp P((char *s));
\r
117 ChessSquare piece;
\r
118 POINT pos; /* window coordinates of current pos */
\r
119 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
120 POINT from; /* board coordinates of the piece's orig pos */
\r
121 POINT to; /* board coordinates of the piece's new pos */
\r
124 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
127 POINT start; /* window coordinates of start pos */
\r
128 POINT pos; /* window coordinates of current pos */
\r
129 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
130 POINT from; /* board coordinates of the piece's orig pos */
\r
134 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };
\r
137 POINT sq[2]; /* board coordinates of from, to squares */
\r
140 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
141 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
142 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
143 static HighlightInfo oldPartnerHighlight = { {{-1, -1}, {-1, -1}} };
\r
145 typedef struct { // [HGM] atomic
\r
146 int fromX, fromY, toX, toY, radius;
\r
149 static ExplodeInfo explodeInfo;
\r
151 /* Window class names */
\r
152 char szAppName[] = "WinBoard";
\r
153 char szConsoleName[] = "WBConsole";
\r
155 /* Title bar text */
\r
156 char szTitle[] = "WinBoard";
\r
157 char szConsoleTitle[] = "I C S Interaction";
\r
160 char *settingsFileName;
\r
161 Boolean saveSettingsOnExit;
\r
162 char installDir[MSG_SIZ];
\r
163 int errorExitStatus;
\r
165 BoardSize boardSize;
\r
166 Boolean chessProgram;
\r
167 //static int boardX, boardY;
\r
168 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
169 int squareSize, lineGap, minorSize, border;
\r
170 static int winW, winH;
\r
171 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
172 static int logoHeight = 0;
\r
173 static char messageText[MESSAGE_TEXT_MAX];
\r
174 static int clockTimerEvent = 0;
\r
175 static int loadGameTimerEvent = 0;
\r
176 static int analysisTimerEvent = 0;
\r
177 static DelayedEventCallback delayedTimerCallback;
\r
178 static int delayedTimerEvent = 0;
\r
179 static int buttonCount = 2;
\r
180 char *icsTextMenuString;
\r
182 char *firstChessProgramNames;
\r
183 char *secondChessProgramNames;
\r
185 #define PALETTESIZE 256
\r
187 HINSTANCE hInst; /* current instance */
\r
188 Boolean alwaysOnTop = FALSE;
\r
190 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
191 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
192 COLORREF markerColor[8] = { 0x00FFFF, 0x0000FF, 0x00FF00, 0xFF0000, 0xFFFF00, 0xFF00FF, 0xFFFFFF, 0x000000 };
\r
194 ColorClass currentColorClass;
\r
196 static HWND savedHwnd;
\r
197 HWND hCommPort = NULL; /* currently open comm port */
\r
198 static HWND hwndPause; /* pause button */
\r
199 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
200 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
201 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
202 explodeBrush, /* [HGM] atomic */
\r
203 markerBrush[8], /* [HGM] markers */
\r
204 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
205 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
206 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
207 static HPEN gridPen = NULL;
\r
208 static HPEN highlightPen = NULL;
\r
209 static HPEN premovePen = NULL;
\r
210 static NPLOGPALETTE pLogPal;
\r
211 static BOOL paletteChanged = FALSE;
\r
212 static HICON iconWhite, iconBlack, iconCurrent;
\r
213 static int doingSizing = FALSE;
\r
214 static int lastSizing = 0;
\r
215 static int prevStderrPort;
\r
216 static HBITMAP userLogo;
\r
218 static HBITMAP liteBackTexture = NULL;
\r
219 static HBITMAP darkBackTexture = NULL;
\r
220 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
221 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
222 static int backTextureSquareSize = 0;
\r
223 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
225 #if __GNUC__ && !defined(_winmajor)
\r
226 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
229 #if defined(_winmajor)
\r
230 #define oldDialog (_winmajor < 4)
\r
232 #define oldDialog 0
\r
236 #define INTERNATIONAL
\r
238 #ifdef INTERNATIONAL
\r
239 # define _(s) T_(s)
\r
245 # define Translate(x, y)
\r
246 # define LoadLanguageFile(s)
\r
249 #ifdef INTERNATIONAL
\r
251 Boolean barbaric; // flag indicating if translation is needed
\r
253 // list of item numbers used in each dialog (used to alter language at run time)
\r
255 #define ABOUTBOX -1 /* not sure why these are needed */
\r
256 #define ABOUTBOX2 -1
\r
258 int dialogItems[][42] = {
\r
259 { ABOUTBOX, IDOK, OPT_MESS, 400 },
\r
260 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed,
\r
261 OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors, IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL },
\r
262 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,
\r
263 OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds,
\r
264 OPT_Ranget, IDOK, IDCANCEL },
\r
265 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
266 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
267 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
268 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
269 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
270 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
271 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
272 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
273 { ABOUTBOX2, IDC_ChessBoard },
\r
274 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
275 OPT_GameListClose, IDC_GameListDoFilter },
\r
276 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
277 { DLG_Error, IDOK },
\r
278 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
279 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
280 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
281 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
282 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
283 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
284 { DLG_IndexNumber, IDC_Index },
\r
285 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
286 { DLG_TypeInName, IDOK, IDCANCEL },
\r
287 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
288 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
289 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
290 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
291 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
292 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
293 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
294 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
295 OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove },
\r
296 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
297 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
298 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
299 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
300 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
301 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
302 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
303 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
304 GPB_General, GPB_Alarm, OPT_AutoCreate },
\r
305 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
306 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
307 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
308 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
309 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
310 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
311 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
312 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid },
\r
313 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
314 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
315 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
316 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
317 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
318 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
319 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,
\r
320 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
321 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
322 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
323 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
324 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,
\r
325 OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7,
\r
326 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
327 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
328 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
329 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
330 { DLG_MoveHistory },
\r
331 { DLG_EvalGraph },
\r
332 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
333 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
334 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
335 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
336 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
337 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
338 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
339 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
340 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
344 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];
\r
345 static int lastChecked;
\r
346 static char oldLanguage[MSG_SIZ], *menuText[10][30];
\r
347 extern int tinyLayout;
\r
348 extern char * menuBarText[][10];
\r
351 LoadLanguageFile(char *name)
\r
352 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
354 int i=0, j=0, n=0, k;
\r
357 if(!name || name[0] == NULLCHAR) return;
\r
358 snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension
\r
359 appData.language = oldLanguage;
\r
360 if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on
\r
361 if((f = fopen(buf, "r")) == NULL) return;
\r
362 while((k = fgetc(f)) != EOF) {
\r
363 if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }
\r
364 languageBuf[i] = k;
\r
366 if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {
\r
368 if(p = strstr(languageBuf + n + 1, "\" === \"")) {
\r
369 if(p > languageBuf+n+2 && p+8 < languageBuf+i) {
\r
370 if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }
\r
371 english[j] = languageBuf + n + 1; *p = 0;
\r
372 foreign[j++] = p + 7; languageBuf[i-1] = 0;
\r
373 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);
\r
378 } else if(i > 0 && languageBuf[i-1] == '\\') {
\r
380 case 'n': k = '\n'; break;
\r
381 case 'r': k = '\r'; break;
\r
382 case 't': k = '\t'; break;
\r
384 languageBuf[--i] = k;
\r
389 barbaric = (j != 0);
\r
390 safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );
\r
395 { // return the translation of the given string
\r
396 // efficiency can be improved a lot...
\r
398 static char buf[MSG_SIZ];
\r
399 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
400 if(!barbaric) return s;
\r
401 if(!s) return ""; // sanity
\r
402 while(english[i]) {
\r
403 if(!strcmp(s, english[i])) return foreign[i];
\r
404 if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending
\r
405 snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion
\r
414 Translate(HWND hDlg, int dialogID)
\r
415 { // translate all text items in the given dialog
\r
417 char buf[MSG_SIZ], *s;
\r
418 if(!barbaric) return;
\r
419 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
420 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
421 GetWindowText( hDlg, buf, MSG_SIZ );
\r
423 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
424 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
425 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
426 if(strlen(buf) == 0) continue;
\r
428 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
433 TranslateOneMenu(int i, HMENU subMenu)
\r
436 static MENUITEMINFO info;
\r
438 info.cbSize = sizeof(MENUITEMINFO);
\r
439 info.fMask = MIIM_STATE | MIIM_TYPE;
\r
440 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
442 info.dwTypeData = buf;
\r
443 info.cch = sizeof(buf);
\r
444 GetMenuItemInfo(subMenu, j, TRUE, &info);
\r
446 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );
\r
447 else menuText[i][j] = strdup(buf); // remember original on first change
\r
449 if(buf[0] == NULLCHAR) continue;
\r
450 info.dwTypeData = T_(buf);
\r
451 info.cch = strlen(buf)+1;
\r
452 SetMenuItemInfo(subMenu, j, TRUE, &info);
\r
458 TranslateMenus(int addLanguage)
\r
461 WIN32_FIND_DATA fileData;
\r
463 #define IDM_English 1970
\r
465 HMENU mainMenu = GetMenu(hwndMain);
\r
466 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
467 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
468 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
469 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
470 TranslateOneMenu(i, subMenu);
\r
472 DrawMenuBar(hwndMain);
\r
475 if(!addLanguage) return;
\r
476 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
477 HMENU mainMenu = GetMenu(hwndMain);
\r
478 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
479 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
480 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
481 i = 0; lastChecked = IDM_English;
\r
483 char *p, *q = fileData.cFileName;
\r
484 int checkFlag = MF_UNCHECKED;
\r
485 languageFile[i] = strdup(q);
\r
486 if(barbaric && !strcmp(oldLanguage, q)) {
\r
487 checkFlag = MF_CHECKED;
\r
488 lastChecked = IDM_English + i + 1;
\r
489 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
491 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
492 p = strstr(fileData.cFileName, ".lng");
\r
494 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
495 } while(FindNextFile(hFind, &fileData));
\r
502 #define IDM_RecentEngines 3000
\r
505 RecentEngineMenu (char *s)
\r
507 if(appData.icsActive) return;
\r
508 if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty
\r
509 HMENU mainMenu = GetMenu(hwndMain);
\r
510 HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu
\r
511 int i=IDM_RecentEngines;
\r
512 recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu
\r
513 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
515 char *p = strchr(s, '\n');
\r
516 if(p == NULL) return; // malformed!
\r
518 AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);
\r
532 int cliWidth, cliHeight;
\r
535 SizeInfo sizeInfo[] =
\r
537 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
538 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
539 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
540 { "petite", 33, 1, 1, 1, 0, 0 },
\r
541 { "slim", 37, 2, 1, 0, 0, 0 },
\r
542 { "small", 40, 2, 1, 0, 0, 0 },
\r
543 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
544 { "middling", 49, 2, 0, 0, 0, 0 },
\r
545 { "average", 54, 2, 0, 0, 0, 0 },
\r
546 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
547 { "medium", 64, 3, 0, 0, 0, 0 },
\r
548 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
549 { "large", 80, 3, 0, 0, 0, 0 },
\r
550 { "big", 87, 3, 0, 0, 0, 0 },
\r
551 { "huge", 95, 3, 0, 0, 0, 0 },
\r
552 { "giant", 108, 3, 0, 0, 0, 0 },
\r
553 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
554 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
555 { NULL, 0, 0, 0, 0, 0, 0 }
\r
558 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
559 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
561 { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
562 { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
563 { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
564 { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
565 { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
566 { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
567 { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
568 { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
569 { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
570 { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
571 { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
572 { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
573 { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
574 { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
575 { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
576 { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
577 { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL), MF (GAMELIST_FONT_ALL) },
\r
578 { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
581 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
590 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
591 #define N_BUTTONS 5
\r
593 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
595 {"<<", IDM_ToStart, NULL, NULL},
\r
596 {"<", IDM_Backward, NULL, NULL},
\r
597 {"P", IDM_Pause, NULL, NULL},
\r
598 {">", IDM_Forward, NULL, NULL},
\r
599 {">>", IDM_ToEnd, NULL, NULL},
\r
602 int tinyLayout = 0, smallLayout = 0;
\r
603 #define MENU_BAR_ITEMS 9
\r
604 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
605 { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },
\r
606 { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },
\r
610 MySound sounds[(int)NSoundClasses];
\r
611 MyTextAttribs textAttribs[(int)NColorClasses];
\r
613 MyColorizeAttribs colorizeAttribs[] = {
\r
614 { (COLORREF)0, 0, N_("Shout Text") },
\r
615 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
616 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
617 { (COLORREF)0, 0, N_("Channel Text") },
\r
618 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
619 { (COLORREF)0, 0, N_("Tell Text") },
\r
620 { (COLORREF)0, 0, N_("Challenge Text") },
\r
621 { (COLORREF)0, 0, N_("Request Text") },
\r
622 { (COLORREF)0, 0, N_("Seek Text") },
\r
623 { (COLORREF)0, 0, N_("Normal Text") },
\r
624 { (COLORREF)0, 0, N_("None") }
\r
629 static char *commentTitle;
\r
630 static char *commentText;
\r
631 static int commentIndex;
\r
632 static Boolean editComment = FALSE;
\r
635 char errorTitle[MSG_SIZ];
\r
636 char errorMessage[2*MSG_SIZ];
\r
637 HWND errorDialog = NULL;
\r
638 BOOLEAN moveErrorMessageUp = FALSE;
\r
639 BOOLEAN consoleEcho = TRUE;
\r
640 CHARFORMAT consoleCF;
\r
641 COLORREF consoleBackgroundColor;
\r
643 char *programVersion;
\r
649 typedef int CPKind;
\r
658 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
661 #define INPUT_SOURCE_BUF_SIZE 4096
\r
663 typedef struct _InputSource {
\r
670 char buf[INPUT_SOURCE_BUF_SIZE];
\r
674 InputCallback func;
\r
675 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
679 InputSource *consoleInputSource;
\r
684 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
685 VOID ConsoleCreate();
\r
687 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
688 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
689 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
690 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
692 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
693 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
694 void ParseIcsTextMenu(char *icsTextMenuString);
\r
695 VOID PopUpNameDialog(char firstchar);
\r
696 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
700 int GameListOptions();
\r
702 int dummy; // [HGM] for obsolete args
\r
704 HWND hwndMain = NULL; /* root window*/
\r
705 HWND hwndConsole = NULL;
\r
706 HWND commentDialog = NULL;
\r
707 HWND moveHistoryDialog = NULL;
\r
708 HWND evalGraphDialog = NULL;
\r
709 HWND engineOutputDialog = NULL;
\r
710 HWND gameListDialog = NULL;
\r
711 HWND editTagsDialog = NULL;
\r
713 int commentUp = FALSE;
\r
715 WindowPlacement wpMain;
\r
716 WindowPlacement wpConsole;
\r
717 WindowPlacement wpComment;
\r
718 WindowPlacement wpMoveHistory;
\r
719 WindowPlacement wpEvalGraph;
\r
720 WindowPlacement wpEngineOutput;
\r
721 WindowPlacement wpGameList;
\r
722 WindowPlacement wpTags;
\r
724 VOID EngineOptionsPopup(); // [HGM] settings
\r
726 VOID GothicPopUp(char *title, VariantClass variant);
\r
728 * Setting "frozen" should disable all user input other than deleting
\r
729 * the window. We do this while engines are initializing themselves.
\r
731 static int frozen = 0;
\r
732 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
738 if (frozen) return;
\r
740 hmenu = GetMenu(hwndMain);
\r
741 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
742 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
744 DrawMenuBar(hwndMain);
\r
747 /* Undo a FreezeUI */
\r
753 if (!frozen) return;
\r
755 hmenu = GetMenu(hwndMain);
\r
756 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
757 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
759 DrawMenuBar(hwndMain);
\r
762 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
764 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
770 #define JAWS_ALT_INTERCEPT
\r
771 #define JAWS_KBUP_NAVIGATION
\r
772 #define JAWS_KBDOWN_NAVIGATION
\r
773 #define JAWS_MENU_ITEMS
\r
774 #define JAWS_SILENCE
\r
775 #define JAWS_REPLAY
\r
777 #define JAWS_COPYRIGHT
\r
778 #define JAWS_DELETE(X) X
\r
779 #define SAYMACHINEMOVE()
\r
783 /*---------------------------------------------------------------------------*\
\r
787 \*---------------------------------------------------------------------------*/
\r
790 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
791 LPSTR lpCmdLine, int nCmdShow)
\r
794 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
795 // INITCOMMONCONTROLSEX ex;
\r
799 LoadLibrary("RICHED32.DLL");
\r
800 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
802 if (!InitApplication(hInstance)) {
\r
805 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
812 // InitCommonControlsEx(&ex);
\r
813 InitCommonControls();
\r
815 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
816 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
817 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
819 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
821 while (GetMessage(&msg, /* message structure */
\r
822 NULL, /* handle of window receiving the message */
\r
823 0, /* lowest message to examine */
\r
824 0)) /* highest message to examine */
\r
827 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
828 // [HGM] navigate: switch between all windows with tab
\r
829 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
830 int i, currentElement = 0;
\r
832 // first determine what element of the chain we come from (if any)
\r
833 if(appData.icsActive) {
\r
834 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
835 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
837 if(engineOutputDialog && EngineOutputIsUp()) {
\r
838 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
839 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
841 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
842 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
844 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
845 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
846 if(msg.hwnd == e1) currentElement = 2; else
\r
847 if(msg.hwnd == e2) currentElement = 3; else
\r
848 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
849 if(msg.hwnd == mh) currentElement = 4; else
\r
850 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
851 if(msg.hwnd == hText) currentElement = 5; else
\r
852 if(msg.hwnd == hInput) currentElement = 6; else
\r
853 for (i = 0; i < N_BUTTONS; i++) {
\r
854 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
857 // determine where to go to
\r
858 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
860 currentElement = (currentElement + direction) % 7;
\r
861 switch(currentElement) {
\r
863 h = hwndMain; break; // passing this case always makes the loop exit
\r
865 h = buttonDesc[0].hwnd; break; // could be NULL
\r
867 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
870 if(!EngineOutputIsUp()) continue;
\r
873 if(!MoveHistoryIsUp()) continue;
\r
875 // case 6: // input to eval graph does not seem to get here!
\r
876 // if(!EvalGraphIsUp()) continue;
\r
877 // h = evalGraphDialog; break;
\r
879 if(!appData.icsActive) continue;
\r
883 if(!appData.icsActive) continue;
\r
889 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
890 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
893 continue; // this message now has been processed
\r
897 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
898 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
899 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
900 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
901 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
902 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
903 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
904 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
905 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
906 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
907 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
908 for(i=0; i<MAX_CHAT; i++)
\r
909 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
912 if(done) continue; // [HGM] chat: end patch
\r
913 TranslateMessage(&msg); /* Translates virtual key codes */
\r
914 DispatchMessage(&msg); /* Dispatches message to window */
\r
919 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
922 /*---------------------------------------------------------------------------*\
\r
924 * Initialization functions
\r
926 \*---------------------------------------------------------------------------*/
\r
930 { // update user logo if necessary
\r
931 static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;
\r
933 if(appData.autoLogo) {
\r
934 curName = UserName();
\r
935 if(strcmp(curName, oldUserName)) {
\r
936 GetCurrentDirectory(MSG_SIZ, dir);
\r
937 SetCurrentDirectory(installDir);
\r
938 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
939 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
940 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
941 if(userLogo == NULL)
\r
942 userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
943 SetCurrentDirectory(dir); /* return to prev directory */
\r
949 InitApplication(HINSTANCE hInstance)
\r
953 /* Fill in window class structure with parameters that describe the */
\r
956 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
957 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
958 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
959 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
960 wc.hInstance = hInstance; /* Owner of this class */
\r
961 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
962 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
963 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
964 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
965 wc.lpszClassName = szAppName; /* Name to register as */
\r
967 /* Register the window class and return success/failure code. */
\r
968 if (!RegisterClass(&wc)) return FALSE;
\r
970 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
971 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
973 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
974 wc.hInstance = hInstance;
\r
975 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
976 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
977 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
978 wc.lpszMenuName = NULL;
\r
979 wc.lpszClassName = szConsoleName;
\r
981 if (!RegisterClass(&wc)) return FALSE;
\r
986 /* Set by InitInstance, used by EnsureOnScreen */
\r
987 int screenHeight, screenWidth;
\r
988 RECT screenGeometry;
\r
991 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
993 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
994 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
995 if (*x > screenGeometry.right - 32) *x = screenGeometry.left;
\r
996 if (*y > screenGeometry.bottom - 32) *y = screenGeometry.top;
\r
997 if (*x < screenGeometry.left + minX) *x = screenGeometry.left + minX;
\r
998 if (*y < screenGeometry.top + minY) *y = screenGeometry.top + minY;
\r
1002 LoadLogo(ChessProgramState *cps, int n, Boolean ics)
\r
1004 char buf[MSG_SIZ], dir[MSG_SIZ];
\r
1005 GetCurrentDirectory(MSG_SIZ, dir);
\r
1006 SetCurrentDirectory(installDir);
\r
1007 if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {
\r
1008 cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1010 if (cps->programLogo == NULL && appData.debugMode) {
\r
1011 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );
\r
1013 } else if(appData.autoLogo) {
\r
1014 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
\r
1015 char *opponent = "";
\r
1016 if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;
\r
1017 if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;
\r
1018 sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);
\r
1019 if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {
\r
1020 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
1021 cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1024 if(appData.directory[n] && appData.directory[n][0]) {
\r
1025 SetCurrentDirectory(appData.directory[n]);
\r
1026 cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1029 SetCurrentDirectory(dir); /* return to prev directory */
\r
1035 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
1036 backTextureSquareSize = 0; // kludge to force recalculation of texturemode
\r
1038 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1039 if(liteBackTexture) DeleteObject(liteBackTexture);
\r
1040 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1041 liteBackTextureMode = appData.liteBackTextureMode;
\r
1043 if (liteBackTexture == NULL && appData.debugMode) {
\r
1044 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1048 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1049 if(darkBackTexture) DeleteObject(darkBackTexture);
\r
1050 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1051 darkBackTextureMode = appData.darkBackTextureMode;
\r
1053 if (darkBackTexture == NULL && appData.debugMode) {
\r
1054 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1059 #ifndef SM_CXVIRTUALSCREEN
\r
1060 #define SM_CXVIRTUALSCREEN 78
\r
1062 #ifndef SM_CYVIRTUALSCREEN
\r
1063 #define SM_CYVIRTUALSCREEN 79
\r
1065 #ifndef SM_XVIRTUALSCREEN
\r
1066 #define SM_XVIRTUALSCREEN 76
\r
1068 #ifndef SM_YVIRTUALSCREEN
\r
1069 #define SM_YVIRTUALSCREEN 77
\r
1075 screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
\r
1076 if( !screenHeight ) screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1077 screenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
\r
1078 if( !screenWidth ) screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1079 screenGeometry.left = GetSystemMetrics(SM_XVIRTUALSCREEN);
\r
1080 screenGeometry.top = GetSystemMetrics(SM_YVIRTUALSCREEN);
\r
1081 screenGeometry.right = screenGeometry.left + screenWidth;
\r
1082 screenGeometry.bottom = screenGeometry.top + screenHeight;
\r
1086 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
1088 HWND hwnd; /* Main window handle. */
\r
1090 WINDOWPLACEMENT wp;
\r
1093 hInst = hInstance; /* Store instance handle in our global variable */
\r
1094 programName = szAppName;
\r
1096 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
1097 *filepart = NULLCHAR;
\r
1098 SetCurrentDirectory(installDir);
\r
1100 GetCurrentDirectory(MSG_SIZ, installDir);
\r
1102 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
1104 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
1105 /* xboard, and older WinBoards, controlled the move sound with the
\r
1106 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
1107 always turn the option on (so that the backend will call us),
\r
1108 then let the user turn the sound off by setting it to silence if
\r
1109 desired. To accommodate old winboard.ini files saved by old
\r
1110 versions of WinBoard, we also turn off the sound if the option
\r
1111 was initially set to false. [HGM] taken out of InitAppData */
\r
1112 if (!appData.ringBellAfterMoves) {
\r
1113 sounds[(int)SoundMove].name = strdup("");
\r
1114 appData.ringBellAfterMoves = TRUE;
\r
1116 if (appData.debugMode) {
\r
1117 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
1118 setbuf(debugFP, NULL);
\r
1121 LoadLanguageFile(appData.language);
\r
1125 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
1126 // InitEngineUCI( installDir, &second );
\r
1128 /* Create a main window for this application instance. */
\r
1129 hwnd = CreateWindow(szAppName, szTitle,
\r
1130 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
1131 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
1132 NULL, NULL, hInstance, NULL);
\r
1135 /* If window could not be created, return "failure" */
\r
1140 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1141 LoadLogo(&first, 0, FALSE);
\r
1142 LoadLogo(&second, 1, appData.icsActive);
\r
1146 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1147 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1148 iconCurrent = iconWhite;
\r
1149 InitDrawingColors();
\r
1151 InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args
\r
1152 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1153 /* Compute window size for each board size, and use the largest
\r
1154 size that fits on this screen as the default. */
\r
1155 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1156 if (boardSize == (BoardSize)-1 &&
\r
1157 winH <= screenHeight
\r
1158 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1159 && winW <= screenWidth) {
\r
1160 boardSize = (BoardSize)ibs;
\r
1164 InitDrawingSizes(boardSize, 0);
\r
1165 RecentEngineMenu(appData.recentEngineList);
\r
1167 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1169 /* [AS] Load textures if specified */
\r
1172 mysrandom( (unsigned) time(NULL) );
\r
1174 /* [AS] Restore layout */
\r
1175 if( wpMoveHistory.visible ) {
\r
1176 MoveHistoryPopUp();
\r
1179 if( wpEvalGraph.visible ) {
\r
1183 if( wpEngineOutput.visible ) {
\r
1184 EngineOutputPopUp();
\r
1187 /* Make the window visible; update its client area; and return "success" */
\r
1188 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1189 wp.length = sizeof(WINDOWPLACEMENT);
\r
1191 wp.showCmd = nCmdShow;
\r
1192 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1193 wp.rcNormalPosition.left = wpMain.x;
\r
1194 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1195 wp.rcNormalPosition.top = wpMain.y;
\r
1196 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1197 SetWindowPlacement(hwndMain, &wp);
\r
1199 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1201 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1202 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1204 if (hwndConsole) {
\r
1206 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1207 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1209 ShowWindow(hwndConsole, nCmdShow);
\r
1210 SetActiveWindow(hwndConsole);
\r
1212 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1213 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1222 HMENU hmenu = GetMenu(hwndMain);
\r
1224 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1225 MF_BYCOMMAND|((appData.icsActive &&
\r
1226 *appData.icsCommPort != NULLCHAR) ?
\r
1227 MF_ENABLED : MF_GRAYED));
\r
1228 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1229 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1230 MF_CHECKED : MF_UNCHECKED));
\r
1231 EnableMenuItem(hmenu, IDM_SaveSelected, MF_GRAYED);
\r
1234 //---------------------------------------------------------------------------------------------------------
\r
1236 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1237 #define XBOARD FALSE
\r
1239 #define OPTCHAR "/"
\r
1240 #define SEPCHAR "="
\r
1241 #define TOPLEVEL 0
\r
1245 // front-end part of option handling
\r
1248 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1250 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1251 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1254 lf->lfEscapement = 0;
\r
1255 lf->lfOrientation = 0;
\r
1256 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1257 lf->lfItalic = mfp->italic;
\r
1258 lf->lfUnderline = mfp->underline;
\r
1259 lf->lfStrikeOut = mfp->strikeout;
\r
1260 lf->lfCharSet = mfp->charset;
\r
1261 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1262 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1263 lf->lfQuality = DEFAULT_QUALITY;
\r
1264 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1265 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1269 CreateFontInMF(MyFont *mf)
\r
1271 LFfromMFP(&mf->lf, &mf->mfp);
\r
1272 if (mf->hf) DeleteObject(mf->hf);
\r
1273 mf->hf = CreateFontIndirect(&mf->lf);
\r
1276 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1278 colorVariable[] = {
\r
1279 &whitePieceColor,
\r
1280 &blackPieceColor,
\r
1281 &lightSquareColor,
\r
1282 &darkSquareColor,
\r
1283 &highlightSquareColor,
\r
1284 &premoveHighlightColor,
\r
1286 &consoleBackgroundColor,
\r
1287 &appData.fontForeColorWhite,
\r
1288 &appData.fontBackColorWhite,
\r
1289 &appData.fontForeColorBlack,
\r
1290 &appData.fontBackColorBlack,
\r
1291 &appData.evalHistColorWhite,
\r
1292 &appData.evalHistColorBlack,
\r
1293 &appData.highlightArrowColor,
\r
1296 /* Command line font name parser. NULL name means do nothing.
\r
1297 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1298 For backward compatibility, syntax without the colon is also
\r
1299 accepted, but font names with digits in them won't work in that case.
\r
1302 ParseFontName(char *name, MyFontParams *mfp)
\r
1305 if (name == NULL) return;
\r
1307 q = strchr(p, ':');
\r
1309 if (q - p >= sizeof(mfp->faceName))
\r
1310 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1311 memcpy(mfp->faceName, p, q - p);
\r
1312 mfp->faceName[q - p] = NULLCHAR;
\r
1315 q = mfp->faceName;
\r
1317 while (*p && !isdigit(*p)) {
\r
1319 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1320 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1322 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1325 if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);
\r
1326 mfp->pointSize = (float) atof(p);
\r
1327 mfp->bold = (strchr(p, 'b') != NULL);
\r
1328 mfp->italic = (strchr(p, 'i') != NULL);
\r
1329 mfp->underline = (strchr(p, 'u') != NULL);
\r
1330 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1331 mfp->charset = DEFAULT_CHARSET;
\r
1332 q = strchr(p, 'c');
\r
1334 mfp->charset = (BYTE) atoi(q+1);
\r
1338 ParseFont(char *name, int number)
\r
1339 { // wrapper to shield back-end from 'font'
\r
1340 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1345 { // in WB we have a 2D array of fonts; this initializes their description
\r
1347 /* Point font array elements to structures and
\r
1348 parse default font names */
\r
1349 for (i=0; i<NUM_FONTS; i++) {
\r
1350 for (j=0; j<NUM_SIZES; j++) {
\r
1351 font[j][i] = &fontRec[j][i];
\r
1352 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1359 { // here we create the actual fonts from the selected descriptions
\r
1361 for (i=0; i<NUM_FONTS; i++) {
\r
1362 for (j=0; j<NUM_SIZES; j++) {
\r
1363 CreateFontInMF(font[j][i]);
\r
1367 /* Color name parser.
\r
1368 X version accepts X color names, but this one
\r
1369 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1371 ParseColorName(char *name)
\r
1373 int red, green, blue, count;
\r
1374 char buf[MSG_SIZ];
\r
1376 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1378 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1379 &red, &green, &blue);
\r
1382 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1383 DisplayError(buf, 0);
\r
1384 return RGB(0, 0, 0);
\r
1386 return PALETTERGB(red, green, blue);
\r
1390 ParseColor(int n, char *name)
\r
1391 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1392 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1396 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1398 char *e = argValue;
\r
1402 if (*e == 'b') eff |= CFE_BOLD;
\r
1403 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1404 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1405 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1406 else if (*e == '#' || isdigit(*e)) break;
\r
1410 *color = ParseColorName(e);
\r
1414 ParseTextAttribs(ColorClass cc, char *s)
\r
1415 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1416 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1417 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1421 ParseBoardSize(void *addr, char *name)
\r
1422 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1423 BoardSize bs = SizeTiny;
\r
1424 while (sizeInfo[bs].name != NULL) {
\r
1425 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1426 *(BoardSize *)addr = bs;
\r
1431 ExitArgError(_("Unrecognized board size value"), name, TRUE);
\r
1436 { // [HGM] import name from appData first
\r
1439 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1440 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1441 textAttribs[cc].sound.data = NULL;
\r
1442 MyLoadSound(&textAttribs[cc].sound);
\r
1444 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1445 textAttribs[cc].sound.name = strdup("");
\r
1446 textAttribs[cc].sound.data = NULL;
\r
1448 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1449 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1450 sounds[sc].data = NULL;
\r
1451 MyLoadSound(&sounds[sc]);
\r
1456 SetCommPortDefaults()
\r
1458 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1459 dcb.DCBlength = sizeof(DCB);
\r
1460 dcb.BaudRate = 9600;
\r
1461 dcb.fBinary = TRUE;
\r
1462 dcb.fParity = FALSE;
\r
1463 dcb.fOutxCtsFlow = FALSE;
\r
1464 dcb.fOutxDsrFlow = FALSE;
\r
1465 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1466 dcb.fDsrSensitivity = FALSE;
\r
1467 dcb.fTXContinueOnXoff = TRUE;
\r
1468 dcb.fOutX = FALSE;
\r
1470 dcb.fNull = FALSE;
\r
1471 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1472 dcb.fAbortOnError = FALSE;
\r
1474 dcb.Parity = SPACEPARITY;
\r
1475 dcb.StopBits = ONESTOPBIT;
\r
1478 // [HGM] args: these three cases taken out to stay in front-end
\r
1480 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1481 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1482 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1483 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1485 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1486 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1487 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1488 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1489 ad->argName, mfp->faceName, mfp->pointSize,
\r
1490 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1491 mfp->bold ? "b" : "",
\r
1492 mfp->italic ? "i" : "",
\r
1493 mfp->underline ? "u" : "",
\r
1494 mfp->strikeout ? "s" : "",
\r
1495 (int)mfp->charset);
\r
1501 { // [HGM] copy the names from the internal WB variables to appData
\r
1504 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1505 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1506 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1507 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1511 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1512 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1513 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1514 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1515 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1516 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1517 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1518 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1519 (ta->effects) ? " " : "",
\r
1520 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1524 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1525 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1526 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1527 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1528 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1532 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1533 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1534 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1538 ParseCommPortSettings(char *s)
\r
1539 { // wrapper to keep dcb from back-end
\r
1540 ParseCommSettings(s, &dcb);
\r
1545 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1546 GetActualPlacement(hwndMain, &wpMain);
\r
1547 GetActualPlacement(hwndConsole, &wpConsole);
\r
1548 GetActualPlacement(commentDialog, &wpComment);
\r
1549 GetActualPlacement(editTagsDialog, &wpTags);
\r
1550 GetActualPlacement(gameListDialog, &wpGameList);
\r
1551 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1552 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1553 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1557 PrintCommPortSettings(FILE *f, char *name)
\r
1558 { // wrapper to shield back-end from DCB
\r
1559 PrintCommSettings(f, name, &dcb);
\r
1563 MySearchPath(char *installDir, char *name, char *fullname)
\r
1565 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1566 if(name[0]== '%') {
\r
1567 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1568 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1569 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1570 *strchr(buf, '%') = 0;
\r
1571 strcat(fullname, getenv(buf));
\r
1572 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1574 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1575 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1576 return (int) strlen(fullname);
\r
1578 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1582 MyGetFullPathName(char *name, char *fullname)
\r
1585 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1590 { // [HGM] args: allows testing if main window is realized from back-end
\r
1591 return hwndMain != NULL;
\r
1595 PopUpStartupDialog()
\r
1599 LoadLanguageFile(appData.language);
\r
1600 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1601 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1602 FreeProcInstance(lpProc);
\r
1605 /*---------------------------------------------------------------------------*\
\r
1607 * GDI board drawing routines
\r
1609 \*---------------------------------------------------------------------------*/
\r
1611 /* [AS] Draw square using background texture */
\r
1612 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1617 return; /* Should never happen! */
\r
1620 SetGraphicsMode( dst, GM_ADVANCED );
\r
1627 /* X reflection */
\r
1632 x.eDx = (FLOAT) dw + dx - 1;
\r
1635 SetWorldTransform( dst, &x );
\r
1638 /* Y reflection */
\r
1644 x.eDy = (FLOAT) dh + dy - 1;
\r
1646 SetWorldTransform( dst, &x );
\r
1654 x.eDx = (FLOAT) dx;
\r
1655 x.eDy = (FLOAT) dy;
\r
1658 SetWorldTransform( dst, &x );
\r
1662 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1670 SetWorldTransform( dst, &x );
\r
1672 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1675 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1677 PM_WP = (int) WhitePawn,
\r
1678 PM_WN = (int) WhiteKnight,
\r
1679 PM_WB = (int) WhiteBishop,
\r
1680 PM_WR = (int) WhiteRook,
\r
1681 PM_WQ = (int) WhiteQueen,
\r
1682 PM_WF = (int) WhiteFerz,
\r
1683 PM_WW = (int) WhiteWazir,
\r
1684 PM_WE = (int) WhiteAlfil,
\r
1685 PM_WM = (int) WhiteMan,
\r
1686 PM_WO = (int) WhiteCannon,
\r
1687 PM_WU = (int) WhiteUnicorn,
\r
1688 PM_WH = (int) WhiteNightrider,
\r
1689 PM_WA = (int) WhiteAngel,
\r
1690 PM_WC = (int) WhiteMarshall,
\r
1691 PM_WAB = (int) WhiteCardinal,
\r
1692 PM_WD = (int) WhiteDragon,
\r
1693 PM_WL = (int) WhiteLance,
\r
1694 PM_WS = (int) WhiteCobra,
\r
1695 PM_WV = (int) WhiteFalcon,
\r
1696 PM_WSG = (int) WhiteSilver,
\r
1697 PM_WG = (int) WhiteGrasshopper,
\r
1698 PM_WK = (int) WhiteKing,
\r
1699 PM_BP = (int) BlackPawn,
\r
1700 PM_BN = (int) BlackKnight,
\r
1701 PM_BB = (int) BlackBishop,
\r
1702 PM_BR = (int) BlackRook,
\r
1703 PM_BQ = (int) BlackQueen,
\r
1704 PM_BF = (int) BlackFerz,
\r
1705 PM_BW = (int) BlackWazir,
\r
1706 PM_BE = (int) BlackAlfil,
\r
1707 PM_BM = (int) BlackMan,
\r
1708 PM_BO = (int) BlackCannon,
\r
1709 PM_BU = (int) BlackUnicorn,
\r
1710 PM_BH = (int) BlackNightrider,
\r
1711 PM_BA = (int) BlackAngel,
\r
1712 PM_BC = (int) BlackMarshall,
\r
1713 PM_BG = (int) BlackGrasshopper,
\r
1714 PM_BAB = (int) BlackCardinal,
\r
1715 PM_BD = (int) BlackDragon,
\r
1716 PM_BL = (int) BlackLance,
\r
1717 PM_BS = (int) BlackCobra,
\r
1718 PM_BV = (int) BlackFalcon,
\r
1719 PM_BSG = (int) BlackSilver,
\r
1720 PM_BK = (int) BlackKing
\r
1723 static HFONT hPieceFont = NULL;
\r
1724 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1725 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1726 static int fontBitmapSquareSize = 0;
\r
1727 static char pieceToFontChar[(int) EmptySquare] =
\r
1728 { 'p', 'n', 'b', 'r', 'q',
\r
1729 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1730 'k', 'o', 'm', 'v', 't', 'w',
\r
1731 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1734 extern BOOL SetCharTable( char *table, const char * map );
\r
1735 /* [HGM] moved to backend.c */
\r
1737 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1740 BYTE r1 = GetRValue( color );
\r
1741 BYTE g1 = GetGValue( color );
\r
1742 BYTE b1 = GetBValue( color );
\r
1748 /* Create a uniform background first */
\r
1749 hbrush = CreateSolidBrush( color );
\r
1750 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1751 FillRect( hdc, &rc, hbrush );
\r
1752 DeleteObject( hbrush );
\r
1755 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1756 int steps = squareSize / 2;
\r
1759 for( i=0; i<steps; i++ ) {
\r
1760 BYTE r = r1 - (r1-r2) * i / steps;
\r
1761 BYTE g = g1 - (g1-g2) * i / steps;
\r
1762 BYTE b = b1 - (b1-b2) * i / steps;
\r
1764 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1765 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1766 FillRect( hdc, &rc, hbrush );
\r
1767 DeleteObject(hbrush);
\r
1770 else if( mode == 2 ) {
\r
1771 /* Diagonal gradient, good more or less for every piece */
\r
1772 POINT triangle[3];
\r
1773 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1774 HBRUSH hbrush_old;
\r
1775 int steps = squareSize;
\r
1778 triangle[0].x = squareSize - steps;
\r
1779 triangle[0].y = squareSize;
\r
1780 triangle[1].x = squareSize;
\r
1781 triangle[1].y = squareSize;
\r
1782 triangle[2].x = squareSize;
\r
1783 triangle[2].y = squareSize - steps;
\r
1785 for( i=0; i<steps; i++ ) {
\r
1786 BYTE r = r1 - (r1-r2) * i / steps;
\r
1787 BYTE g = g1 - (g1-g2) * i / steps;
\r
1788 BYTE b = b1 - (b1-b2) * i / steps;
\r
1790 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1791 hbrush_old = SelectObject( hdc, hbrush );
\r
1792 Polygon( hdc, triangle, 3 );
\r
1793 SelectObject( hdc, hbrush_old );
\r
1794 DeleteObject(hbrush);
\r
1799 SelectObject( hdc, hpen );
\r
1804 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1805 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1806 piece: follow the steps as explained below.
\r
1808 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1812 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1818 int backColor = whitePieceColor;
\r
1819 int foreColor = blackPieceColor;
\r
1821 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1822 backColor = appData.fontBackColorWhite;
\r
1823 foreColor = appData.fontForeColorWhite;
\r
1825 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1826 backColor = appData.fontBackColorBlack;
\r
1827 foreColor = appData.fontForeColorBlack;
\r
1831 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1833 hbm_old = SelectObject( hdc, hbm );
\r
1837 rc.right = squareSize;
\r
1838 rc.bottom = squareSize;
\r
1840 /* Step 1: background is now black */
\r
1841 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1843 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1845 pt.x = (squareSize - sz.cx) / 2;
\r
1846 pt.y = (squareSize - sz.cy) / 2;
\r
1848 SetBkMode( hdc, TRANSPARENT );
\r
1849 SetTextColor( hdc, chroma );
\r
1850 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1851 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1853 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1854 /* Step 3: the area outside the piece is filled with white */
\r
1855 // FloodFill( hdc, 0, 0, chroma );
\r
1856 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1857 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1858 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1859 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1860 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1862 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1863 but if the start point is not inside the piece we're lost!
\r
1864 There should be a better way to do this... if we could create a region or path
\r
1865 from the fill operation we would be fine for example.
\r
1867 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1868 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1870 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1871 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1872 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1874 SelectObject( dc2, bm2 );
\r
1875 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1876 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1877 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1878 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1879 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1882 DeleteObject( bm2 );
\r
1885 SetTextColor( hdc, 0 );
\r
1887 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1888 draw the piece again in black for safety.
\r
1890 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1892 SelectObject( hdc, hbm_old );
\r
1894 if( hPieceMask[index] != NULL ) {
\r
1895 DeleteObject( hPieceMask[index] );
\r
1898 hPieceMask[index] = hbm;
\r
1901 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1903 SelectObject( hdc, hbm );
\r
1906 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1907 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1908 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1910 SelectObject( dc1, hPieceMask[index] );
\r
1911 SelectObject( dc2, bm2 );
\r
1912 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1913 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1916 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1917 the piece background and deletes (makes transparent) the rest.
\r
1918 Thanks to that mask, we are free to paint the background with the greates
\r
1919 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1920 We use this, to make gradients and give the pieces a "roundish" look.
\r
1922 SetPieceBackground( hdc, backColor, 2 );
\r
1923 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1927 DeleteObject( bm2 );
\r
1930 SetTextColor( hdc, foreColor );
\r
1931 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1933 SelectObject( hdc, hbm_old );
\r
1935 if( hPieceFace[index] != NULL ) {
\r
1936 DeleteObject( hPieceFace[index] );
\r
1939 hPieceFace[index] = hbm;
\r
1942 static int TranslatePieceToFontPiece( int piece )
\r
1972 case BlackMarshall:
\r
1976 case BlackNightrider:
\r
1982 case BlackUnicorn:
\r
1986 case BlackGrasshopper:
\r
1998 case BlackCardinal:
\r
2005 case WhiteMarshall:
\r
2009 case WhiteNightrider:
\r
2015 case WhiteUnicorn:
\r
2019 case WhiteGrasshopper:
\r
2031 case WhiteCardinal:
\r
2040 void CreatePiecesFromFont()
\r
2043 HDC hdc_window = NULL;
\r
2049 if( fontBitmapSquareSize < 0 ) {
\r
2050 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
2054 if( !appData.useFont || appData.renderPiecesWithFont == NULL ||
\r
2055 appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
2056 fontBitmapSquareSize = -1;
\r
2060 if( fontBitmapSquareSize != squareSize ) {
\r
2061 hdc_window = GetDC( hwndMain );
\r
2062 hdc = CreateCompatibleDC( hdc_window );
\r
2064 if( hPieceFont != NULL ) {
\r
2065 DeleteObject( hPieceFont );
\r
2068 for( i=0; i<=(int)BlackKing; i++ ) {
\r
2069 hPieceMask[i] = NULL;
\r
2070 hPieceFace[i] = NULL;
\r
2076 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
2077 fontHeight = appData.fontPieceSize;
\r
2080 fontHeight = (fontHeight * squareSize) / 100;
\r
2082 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2084 lf.lfEscapement = 0;
\r
2085 lf.lfOrientation = 0;
\r
2086 lf.lfWeight = FW_NORMAL;
\r
2088 lf.lfUnderline = 0;
\r
2089 lf.lfStrikeOut = 0;
\r
2090 lf.lfCharSet = DEFAULT_CHARSET;
\r
2091 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2092 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2093 lf.lfQuality = PROOF_QUALITY;
\r
2094 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2095 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2096 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2098 hPieceFont = CreateFontIndirect( &lf );
\r
2100 if( hPieceFont == NULL ) {
\r
2101 fontBitmapSquareSize = -2;
\r
2104 /* Setup font-to-piece character table */
\r
2105 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2106 /* No (or wrong) global settings, try to detect the font */
\r
2107 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2109 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2111 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2112 /* DiagramTT* family */
\r
2113 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2115 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2116 /* Fairy symbols */
\r
2117 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2119 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2120 /* Good Companion (Some characters get warped as literal :-( */
\r
2121 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2122 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2123 SetCharTable(pieceToFontChar, s);
\r
2126 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2127 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2131 /* Create bitmaps */
\r
2132 hfont_old = SelectObject( hdc, hPieceFont );
\r
2133 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2134 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2135 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2137 SelectObject( hdc, hfont_old );
\r
2139 fontBitmapSquareSize = squareSize;
\r
2143 if( hdc != NULL ) {
\r
2147 if( hdc_window != NULL ) {
\r
2148 ReleaseDC( hwndMain, hdc_window );
\r
2153 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2155 char name[128], buf[MSG_SIZ];
\r
2157 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2158 if(appData.pieceDirectory[0]) {
\r
2160 snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);
\r
2161 res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
2162 if(res) return res;
\r
2164 if (gameInfo.event &&
\r
2165 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2166 strcmp(name, "k80s") == 0) {
\r
2167 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2169 return LoadBitmap(hinst, name);
\r
2173 /* Insert a color into the program's logical palette
\r
2174 structure. This code assumes the given color is
\r
2175 the result of the RGB or PALETTERGB macro, and it
\r
2176 knows how those macros work (which is documented).
\r
2179 InsertInPalette(COLORREF color)
\r
2181 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2183 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2184 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2185 pLogPal->palNumEntries--;
\r
2189 pe->peFlags = (char) 0;
\r
2190 pe->peRed = (char) (0xFF & color);
\r
2191 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2192 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2198 InitDrawingColors()
\r
2201 if (pLogPal == NULL) {
\r
2202 /* Allocate enough memory for a logical palette with
\r
2203 * PALETTESIZE entries and set the size and version fields
\r
2204 * of the logical palette structure.
\r
2206 pLogPal = (NPLOGPALETTE)
\r
2207 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2208 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2209 pLogPal->palVersion = 0x300;
\r
2211 pLogPal->palNumEntries = 0;
\r
2213 InsertInPalette(lightSquareColor);
\r
2214 InsertInPalette(darkSquareColor);
\r
2215 InsertInPalette(whitePieceColor);
\r
2216 InsertInPalette(blackPieceColor);
\r
2217 InsertInPalette(highlightSquareColor);
\r
2218 InsertInPalette(premoveHighlightColor);
\r
2220 /* create a logical color palette according the information
\r
2221 * in the LOGPALETTE structure.
\r
2223 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2225 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2226 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2227 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2228 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2229 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2230 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2231 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2232 for(i=0; i<8;i++) markerBrush[i] = CreateSolidBrush(markerColor[i]); // [HGM] markers
\r
2234 /* [AS] Force rendering of the font-based pieces */
\r
2235 if( fontBitmapSquareSize > 0 ) {
\r
2236 fontBitmapSquareSize = 0;
\r
2242 BoardWidth(int boardSize, int n)
\r
2243 { /* [HGM] argument n added to allow different width and height */
\r
2244 int lineGap = sizeInfo[boardSize].lineGap;
\r
2246 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2247 lineGap = appData.overrideLineGap;
\r
2250 return (n + 1) * lineGap +
\r
2251 n * sizeInfo[boardSize].squareSize;
\r
2254 /* Respond to board resize by dragging edge */
\r
2256 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2258 BoardSize newSize = NUM_SIZES - 1;
\r
2259 static int recurse = 0;
\r
2260 if (IsIconic(hwndMain)) return;
\r
2261 if (recurse > 0) return;
\r
2263 while (newSize > 0) {
\r
2264 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2265 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2266 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2269 boardSize = newSize;
\r
2270 InitDrawingSizes(boardSize, flags);
\r
2275 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2278 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2280 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2281 ChessSquare piece;
\r
2282 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2284 SIZE clockSize, messageSize;
\r
2286 char buf[MSG_SIZ];
\r
2288 HMENU hmenu = GetMenu(hwndMain);
\r
2289 RECT crect, wrect, oldRect;
\r
2291 LOGBRUSH logbrush;
\r
2292 VariantClass v = gameInfo.variant;
\r
2294 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2295 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2297 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2298 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2299 if(boardSize == -1) return; // no size defined yet; abort (to allow early call of InitPosition)
\r
2300 oldBoardSize = boardSize;
\r
2302 if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)
\r
2303 { // correct board size to one where built-in pieces exist
\r
2304 if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)
\r
2305 && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range
\r
2307 || (v == VariantShogi && boardSize != SizeModerate) // Japanese-style Shogi
\r
2308 || v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan
\r
2309 || v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy || v == VariantLion ) {
\r
2310 if(boardSize < SizeMediocre) boardSize = SizePetite; else
\r
2311 if(boardSize > SizeModerate) boardSize = SizeBulky; else
\r
2312 boardSize = SizeMiddling;
\r
2315 if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite
\r
2317 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2318 oldRect.top = wpMain.y;
\r
2319 oldRect.right = wpMain.x + wpMain.width;
\r
2320 oldRect.bottom = wpMain.y + wpMain.height;
\r
2322 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2323 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2324 squareSize = sizeInfo[boardSize].squareSize;
\r
2325 lineGap = sizeInfo[boardSize].lineGap;
\r
2326 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2327 border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;
\r
2329 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2330 lineGap = appData.overrideLineGap;
\r
2333 if (tinyLayout != oldTinyLayout) {
\r
2334 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2336 style &= ~WS_SYSMENU;
\r
2337 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2338 "&Minimize\tCtrl+F4");
\r
2340 style |= WS_SYSMENU;
\r
2341 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2343 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2345 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2346 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2347 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2349 DrawMenuBar(hwndMain);
\r
2352 boardWidth = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;
\r
2353 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;
\r
2355 /* Get text area sizes */
\r
2356 hdc = GetDC(hwndMain);
\r
2357 if (appData.clockMode) {
\r
2358 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2360 snprintf(buf, MSG_SIZ, _("White"));
\r
2362 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2363 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2364 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2365 str = _("We only care about the height here");
\r
2366 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2367 SelectObject(hdc, oldFont);
\r
2368 ReleaseDC(hwndMain, hdc);
\r
2370 /* Compute where everything goes */
\r
2371 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2372 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2373 logoHeight = 2*clockSize.cy;
\r
2374 leftLogoRect.left = OUTER_MARGIN;
\r
2375 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2376 leftLogoRect.top = OUTER_MARGIN;
\r
2377 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2379 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2380 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2381 rightLogoRect.top = OUTER_MARGIN;
\r
2382 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2385 whiteRect.left = leftLogoRect.right;
\r
2386 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2387 whiteRect.top = OUTER_MARGIN;
\r
2388 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2390 blackRect.right = rightLogoRect.left;
\r
2391 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2392 blackRect.top = whiteRect.top;
\r
2393 blackRect.bottom = whiteRect.bottom;
\r
2395 whiteRect.left = OUTER_MARGIN;
\r
2396 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2397 whiteRect.top = OUTER_MARGIN;
\r
2398 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2400 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2401 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2402 blackRect.top = whiteRect.top;
\r
2403 blackRect.bottom = whiteRect.bottom;
\r
2405 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2408 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2409 if (appData.showButtonBar) {
\r
2410 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2411 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2413 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2415 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2416 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2418 boardRect.left = OUTER_MARGIN;
\r
2419 boardRect.right = boardRect.left + boardWidth;
\r
2420 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2421 boardRect.bottom = boardRect.top + boardHeight;
\r
2423 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2424 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2425 oldTinyLayout = tinyLayout;
\r
2426 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2427 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2428 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2429 winW *= 1 + twoBoards;
\r
2430 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2431 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2432 wpMain.height = winH; // without disturbing window attachments
\r
2433 GetWindowRect(hwndMain, &wrect);
\r
2434 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2435 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2437 // [HGM] placement: let attached windows follow size change.
\r
2438 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2439 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2440 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2441 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2442 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2444 /* compensate if menu bar wrapped */
\r
2445 GetClientRect(hwndMain, &crect);
\r
2446 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2447 wpMain.height += offby;
\r
2449 case WMSZ_TOPLEFT:
\r
2450 SetWindowPos(hwndMain, NULL,
\r
2451 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2452 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2455 case WMSZ_TOPRIGHT:
\r
2457 SetWindowPos(hwndMain, NULL,
\r
2458 wrect.left, wrect.bottom - wpMain.height,
\r
2459 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2462 case WMSZ_BOTTOMLEFT:
\r
2464 SetWindowPos(hwndMain, NULL,
\r
2465 wrect.right - wpMain.width, wrect.top,
\r
2466 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2469 case WMSZ_BOTTOMRIGHT:
\r
2473 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2474 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2479 for (i = 0; i < N_BUTTONS; i++) {
\r
2480 if (buttonDesc[i].hwnd != NULL) {
\r
2481 DestroyWindow(buttonDesc[i].hwnd);
\r
2482 buttonDesc[i].hwnd = NULL;
\r
2484 if (appData.showButtonBar) {
\r
2485 buttonDesc[i].hwnd =
\r
2486 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2487 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2488 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2489 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2490 (HMENU) buttonDesc[i].id,
\r
2491 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2493 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2494 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2495 MAKELPARAM(FALSE, 0));
\r
2497 if (buttonDesc[i].id == IDM_Pause)
\r
2498 hwndPause = buttonDesc[i].hwnd;
\r
2499 buttonDesc[i].wndproc = (WNDPROC)
\r
2500 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2503 if (gridPen != NULL) DeleteObject(gridPen);
\r
2504 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2505 if (premovePen != NULL) DeleteObject(premovePen);
\r
2506 if (lineGap != 0) {
\r
2507 logbrush.lbStyle = BS_SOLID;
\r
2508 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2510 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2511 lineGap, &logbrush, 0, NULL);
\r
2512 logbrush.lbColor = highlightSquareColor;
\r
2514 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2515 lineGap, &logbrush, 0, NULL);
\r
2517 logbrush.lbColor = premoveHighlightColor;
\r
2519 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2520 lineGap, &logbrush, 0, NULL);
\r
2522 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2523 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2524 gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;
\r
2525 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2526 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;
\r
2527 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2528 BOARD_WIDTH * (squareSize + lineGap) + border;
\r
2529 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2531 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2532 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;
\r
2533 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2534 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2535 lineGap / 2 + (i * (squareSize + lineGap)) + border;
\r
2536 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2537 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;
\r
2538 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2542 /* [HGM] Licensing requirement */
\r
2544 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2547 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2549 GothicPopUp( "", VariantNormal);
\r
2552 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2554 /* Load piece bitmaps for this board size */
\r
2555 for (i=0; i<=2; i++) {
\r
2556 for (piece = WhitePawn;
\r
2557 (int) piece < (int) BlackPawn;
\r
2558 piece = (ChessSquare) ((int) piece + 1)) {
\r
2559 if (pieceBitmap[i][piece] != NULL)
\r
2560 DeleteObject(pieceBitmap[i][piece]);
\r
2564 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2565 // Orthodox Chess pieces
\r
2566 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2567 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2568 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2569 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2570 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2571 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2572 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2573 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2574 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2575 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2576 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2577 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2578 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2579 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2580 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2581 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2582 // in Shogi, Hijack the unused Queen for Lance
\r
2583 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2584 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2585 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2587 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2588 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2589 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2592 if(squareSize <= 72 && squareSize >= 33) {
\r
2593 /* A & C are available in most sizes now */
\r
2594 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2595 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2596 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2597 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2598 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2599 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2600 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2601 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2602 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2603 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2604 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2605 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2606 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2607 } else { // Smirf-like
\r
2608 if(gameInfo.variant == VariantSChess) {
\r
2609 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2610 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2611 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2613 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2614 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2615 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2618 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2619 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2620 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2621 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2622 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2623 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2624 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2625 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2626 } else { // WinBoard standard
\r
2627 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2628 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2629 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2634 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2635 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2636 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2637 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2638 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2639 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2640 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2641 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2642 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2643 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2644 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2645 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2646 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2647 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2648 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2649 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2650 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2651 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2652 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2653 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2654 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2655 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2656 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2657 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2658 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2659 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2660 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2661 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2662 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2663 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2664 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2665 pieceBitmap[0][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "s");
\r
2666 pieceBitmap[1][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "o");
\r
2667 pieceBitmap[2][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "w");
\r
2669 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2670 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2671 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2672 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2673 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2674 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2675 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2676 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2677 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2678 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2679 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2680 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2681 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2683 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2684 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2685 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2686 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2687 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2688 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2689 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2690 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2691 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2692 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2693 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2694 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2697 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2698 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2699 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2700 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2701 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2702 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2703 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2704 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2705 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2706 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2707 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2708 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2709 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2710 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2711 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2715 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2716 /* special Shogi support in this size */
\r
2717 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2718 for (piece = WhitePawn;
\r
2719 (int) piece < (int) BlackPawn;
\r
2720 piece = (ChessSquare) ((int) piece + 1)) {
\r
2721 if (pieceBitmap[i][piece] != NULL)
\r
2722 DeleteObject(pieceBitmap[i][piece]);
\r
2725 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2726 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2727 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2728 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2729 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2730 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2731 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2732 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2733 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2734 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2735 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2736 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2737 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2738 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2739 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2740 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2741 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2742 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2743 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2744 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2745 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2746 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2747 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2748 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2749 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2750 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2751 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2752 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2753 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2754 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2755 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2756 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2757 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2758 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2759 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2760 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2761 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2762 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2763 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2764 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2765 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2766 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2772 PieceBitmap(ChessSquare p, int kind)
\r
2774 if ((int) p >= (int) BlackPawn)
\r
2775 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2777 return pieceBitmap[kind][(int) p];
\r
2780 /***************************************************************/
\r
2782 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2783 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2785 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2786 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2790 SquareToPos(int row, int column, int * x, int * y)
\r
2793 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
2794 *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;
\r
2796 *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;
\r
2797 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
2802 DrawCoordsOnDC(HDC hdc)
\r
2804 static char files[] = "0123456789012345678901221098765432109876543210";
\r
2805 static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";
\r
2806 char str[2] = { NULLCHAR, NULLCHAR };
\r
2807 int oldMode, oldAlign, x, y, start, i;
\r
2811 if (!appData.showCoords)
\r
2814 start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;
\r
2816 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2817 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2818 oldAlign = GetTextAlign(hdc);
\r
2819 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2821 y = boardRect.top + lineGap;
\r
2822 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2825 SetTextAlign(hdc, TA_RIGHT|TA_TOP);
\r
2826 x += border - lineGap - 4; y += squareSize - 6;
\r
2828 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2829 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2830 str[0] = files[start + i];
\r
2831 ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);
\r
2832 y += squareSize + lineGap;
\r
2835 start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;
\r
2838 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2839 x += -border + 4; y += border - squareSize + 6;
\r
2841 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2842 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2843 str[0] = ranks[start + i];
\r
2844 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2845 x += squareSize + lineGap;
\r
2848 SelectObject(hdc, oldBrush);
\r
2849 SetBkMode(hdc, oldMode);
\r
2850 SetTextAlign(hdc, oldAlign);
\r
2851 SelectObject(hdc, oldFont);
\r
2855 DrawGridOnDC(HDC hdc)
\r
2859 if (lineGap != 0) {
\r
2860 oldPen = SelectObject(hdc, gridPen);
\r
2861 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2862 SelectObject(hdc, oldPen);
\r
2866 #define HIGHLIGHT_PEN 0
\r
2867 #define PREMOVE_PEN 1
\r
2870 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2873 HPEN oldPen, hPen;
\r
2874 if (lineGap == 0) return;
\r
2876 x1 = boardRect.left +
\r
2877 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;
\r
2878 y1 = boardRect.top +
\r
2879 lineGap/2 + y * (squareSize + lineGap) + border;
\r
2881 x1 = boardRect.left +
\r
2882 lineGap/2 + x * (squareSize + lineGap) + border;
\r
2883 y1 = boardRect.top +
\r
2884 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;
\r
2886 hPen = pen ? premovePen : highlightPen;
\r
2887 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2888 MoveToEx(hdc, x1, y1, NULL);
\r
2889 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2890 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2891 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2892 LineTo(hdc, x1, y1);
\r
2893 SelectObject(hdc, oldPen);
\r
2897 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2900 for (i=0; i<2; i++) {
\r
2901 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2902 DrawHighlightOnDC(hdc, TRUE,
\r
2903 h->sq[i].x, h->sq[i].y,
\r
2908 /* Note: sqcolor is used only in monoMode */
\r
2909 /* Note that this code is largely duplicated in woptions.c,
\r
2910 function DrawSampleSquare, so that needs to be updated too */
\r
2912 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2914 HBITMAP oldBitmap;
\r
2918 if (appData.blindfold) return;
\r
2920 /* [AS] Use font-based pieces if needed */
\r
2921 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2922 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2923 CreatePiecesFromFont();
\r
2925 if( fontBitmapSquareSize == squareSize ) {
\r
2926 int index = TranslatePieceToFontPiece(piece);
\r
2928 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2930 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2931 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2935 squareSize, squareSize,
\r
2940 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2942 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2943 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2947 squareSize, squareSize,
\r
2956 if (appData.monoMode) {
\r
2957 SelectObject(tmphdc, PieceBitmap(piece,
\r
2958 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2959 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2960 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2962 HBRUSH xBrush = whitePieceBrush;
\r
2963 tmpSize = squareSize;
\r
2964 if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);
\r
2966 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2967 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2968 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2969 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2970 x += (squareSize - minorSize)>>1;
\r
2971 y += squareSize - minorSize - 2;
\r
2972 tmpSize = minorSize;
\r
2974 if (color || appData.allWhite ) {
\r
2975 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2977 oldBrush = SelectObject(hdc, xBrush);
\r
2978 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2979 if(appData.upsideDown && color==flipView)
\r
2980 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2982 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2983 /* Use black for outline of white pieces */
\r
2984 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2985 if(appData.upsideDown && color==flipView)
\r
2986 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2988 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2989 } else if(appData.pieceDirectory[0]) {
\r
2990 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2991 oldBrush = SelectObject(hdc, xBrush);
\r
2992 if(appData.upsideDown && color==flipView)
\r
2993 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2995 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2996 SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2997 if(appData.upsideDown && color==flipView)
\r
2998 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
3000 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
3002 /* Use square color for details of black pieces */
\r
3003 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
3004 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
3005 if(appData.upsideDown && !flipView)
\r
3006 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
3008 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
3010 SelectObject(hdc, oldBrush);
\r
3011 SelectObject(tmphdc, oldBitmap);
\r
3015 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
3016 int GetBackTextureMode( int algo )
\r
3018 int result = BACK_TEXTURE_MODE_DISABLED;
\r
3022 case BACK_TEXTURE_MODE_PLAIN:
\r
3023 result = 1; /* Always use identity map */
\r
3025 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
3026 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
3034 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
3035 to handle redraws cleanly (as random numbers would always be different).
\r
3037 VOID RebuildTextureSquareInfo()
\r
3047 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
3049 if( liteBackTexture != NULL ) {
\r
3050 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3051 lite_w = bi.bmWidth;
\r
3052 lite_h = bi.bmHeight;
\r
3056 if( darkBackTexture != NULL ) {
\r
3057 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3058 dark_w = bi.bmWidth;
\r
3059 dark_h = bi.bmHeight;
\r
3063 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
3064 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
3065 if( (col + row) & 1 ) {
\r
3067 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
3068 if( lite_w >= squareSize*BOARD_WIDTH )
\r
3069 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
3071 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
3072 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
3073 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
3075 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
3076 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
3081 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
3082 if( dark_w >= squareSize*BOARD_WIDTH )
\r
3083 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
3085 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
3086 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
3087 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
3089 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
3090 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
3097 /* [AS] Arrow highlighting support */
\r
3099 static double A_WIDTH = 5; /* Width of arrow body */
\r
3101 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
3102 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
3104 static double Sqr( double x )
\r
3109 static int Round( double x )
\r
3111 return (int) (x + 0.5);
\r
3114 /* Draw an arrow between two points using current settings */
\r
3115 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
3118 double dx, dy, j, k, x, y;
\r
3120 if( d_x == s_x ) {
\r
3121 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3123 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
3126 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
3127 arrow[1].y = d_y - h;
\r
3129 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3130 arrow[2].y = d_y - h;
\r
3135 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3136 arrow[5].y = d_y - h;
\r
3138 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3139 arrow[4].y = d_y - h;
\r
3141 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3144 else if( d_y == s_y ) {
\r
3145 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3148 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3150 arrow[1].x = d_x - w;
\r
3151 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3153 arrow[2].x = d_x - w;
\r
3154 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3159 arrow[5].x = d_x - w;
\r
3160 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3162 arrow[4].x = d_x - w;
\r
3163 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3166 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3169 /* [AS] Needed a lot of paper for this! :-) */
\r
3170 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3171 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3173 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3175 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3180 arrow[0].x = Round(x - j);
\r
3181 arrow[0].y = Round(y + j*dx);
\r
3183 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3184 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3187 x = (double) d_x - k;
\r
3188 y = (double) d_y - k*dy;
\r
3191 x = (double) d_x + k;
\r
3192 y = (double) d_y + k*dy;
\r
3195 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3197 arrow[6].x = Round(x - j);
\r
3198 arrow[6].y = Round(y + j*dx);
\r
3200 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3201 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3203 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3204 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3209 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3210 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3213 Polygon( hdc, arrow, 7 );
\r
3216 /* [AS] Draw an arrow between two squares */
\r
3217 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3219 int s_x, s_y, d_x, d_y;
\r
3226 if( s_col == d_col && s_row == d_row ) {
\r
3230 /* Get source and destination points */
\r
3231 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3232 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3235 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3237 else if( d_y < s_y ) {
\r
3238 d_y += squareSize / 2 + squareSize / 4;
\r
3241 d_y += squareSize / 2;
\r
3245 d_x += squareSize / 2 - squareSize / 4;
\r
3247 else if( d_x < s_x ) {
\r
3248 d_x += squareSize / 2 + squareSize / 4;
\r
3251 d_x += squareSize / 2;
\r
3254 s_x += squareSize / 2;
\r
3255 s_y += squareSize / 2;
\r
3257 /* Adjust width */
\r
3258 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3261 stLB.lbStyle = BS_SOLID;
\r
3262 stLB.lbColor = appData.highlightArrowColor;
\r
3265 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3266 holdpen = SelectObject( hdc, hpen );
\r
3267 hbrush = CreateBrushIndirect( &stLB );
\r
3268 holdbrush = SelectObject( hdc, hbrush );
\r
3270 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3272 SelectObject( hdc, holdpen );
\r
3273 SelectObject( hdc, holdbrush );
\r
3274 DeleteObject( hpen );
\r
3275 DeleteObject( hbrush );
\r
3278 BOOL HasHighlightInfo()
\r
3280 BOOL result = FALSE;
\r
3282 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3283 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3294 BOOL IsDrawArrowEnabled()
\r
3296 BOOL result = FALSE;
\r
3298 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3305 VOID DrawArrowHighlight( HDC hdc )
\r
3307 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3308 DrawArrowBetweenSquares( hdc,
\r
3309 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3310 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3314 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3316 HRGN result = NULL;
\r
3318 if( HasHighlightInfo() ) {
\r
3319 int x1, y1, x2, y2;
\r
3320 int sx, sy, dx, dy;
\r
3322 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3323 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3325 sx = MIN( x1, x2 );
\r
3326 sy = MIN( y1, y2 );
\r
3327 dx = MAX( x1, x2 ) + squareSize;
\r
3328 dy = MAX( y1, y2 ) + squareSize;
\r
3330 result = CreateRectRgn( sx, sy, dx, dy );
\r
3337 Warning: this function modifies the behavior of several other functions.
\r
3339 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3340 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3341 repaint is scattered all over the place, which is not good for features such as
\r
3342 "arrow highlighting" that require a full repaint of the board.
\r
3344 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3345 user interaction, when speed is not so important) but especially to avoid errors
\r
3346 in the displayed graphics.
\r
3348 In such patched places, I always try refer to this function so there is a single
\r
3349 place to maintain knowledge.
\r
3351 To restore the original behavior, just return FALSE unconditionally.
\r
3353 BOOL IsFullRepaintPreferrable()
\r
3355 BOOL result = FALSE;
\r
3357 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3358 /* Arrow may appear on the board */
\r
3366 This function is called by DrawPosition to know whether a full repaint must
\r
3369 Only DrawPosition may directly call this function, which makes use of
\r
3370 some state information. Other function should call DrawPosition specifying
\r
3371 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3373 BOOL DrawPositionNeedsFullRepaint()
\r
3375 BOOL result = FALSE;
\r
3378 Probably a slightly better policy would be to trigger a full repaint
\r
3379 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3380 but animation is fast enough that it's difficult to notice.
\r
3382 if( animInfo.piece == EmptySquare ) {
\r
3383 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3391 static HBITMAP borderBitmap;
\r
3394 DrawBackgroundOnDC(HDC hdc)
\r
3400 static char oldBorder[MSG_SIZ];
\r
3401 int w = 600, h = 600, mode;
\r
3403 if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid
\r
3404 strncpy(oldBorder, appData.border, MSG_SIZ-1);
\r
3405 borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
3407 if(borderBitmap == NULL) { // loading failed, use white
\r
3408 FillRect( hdc, &boardRect, whitePieceBrush );
\r
3411 tmphdc = CreateCompatibleDC(hdc);
\r
3412 hbm = SelectObject(tmphdc, borderBitmap);
\r
3413 if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {
\r
3417 mode = SetStretchBltMode(hdc, COLORONCOLOR);
\r
3418 StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left,
\r
3419 boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3420 SetStretchBltMode(hdc, mode);
\r
3421 SelectObject(tmphdc, hbm);
\r
3426 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3428 int row, column, x, y, square_color, piece_color;
\r
3429 ChessSquare piece;
\r
3431 HDC texture_hdc = NULL;
\r
3433 /* [AS] Initialize background textures if needed */
\r
3434 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3435 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3436 if( backTextureSquareSize != squareSize
\r
3437 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3438 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3439 backTextureSquareSize = squareSize;
\r
3440 RebuildTextureSquareInfo();
\r
3443 texture_hdc = CreateCompatibleDC( hdc );
\r
3446 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3447 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3449 SquareToPos(row, column, &x, &y);
\r
3451 piece = board[row][column];
\r
3453 square_color = ((column + row) % 2) == 1;
\r
3454 if( gameInfo.variant == VariantXiangqi ) {
\r
3455 square_color = !InPalace(row, column);
\r
3456 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3457 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3459 piece_color = (int) piece < (int) BlackPawn;
\r
3462 /* [HGM] holdings file: light square or black */
\r
3463 if(column == BOARD_LEFT-2) {
\r
3464 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3467 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3471 if(column == BOARD_RGHT + 1 ) {
\r
3472 if( row < gameInfo.holdingsSize )
\r
3475 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3479 if(column == BOARD_LEFT-1 ) /* left align */
\r
3480 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3481 else if( column == BOARD_RGHT) /* right align */
\r
3482 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3483 else if( piece == DarkSquare) DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3485 if (appData.monoMode) {
\r
3486 if (piece == EmptySquare) {
\r
3487 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3488 square_color ? WHITENESS : BLACKNESS);
\r
3490 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3493 else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {
\r
3494 /* [AS] Draw the square using a texture bitmap */
\r
3495 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3496 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3497 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3500 squareSize, squareSize,
\r
3503 backTextureSquareInfo[r][c].mode,
\r
3504 backTextureSquareInfo[r][c].x,
\r
3505 backTextureSquareInfo[r][c].y );
\r
3507 SelectObject( texture_hdc, hbm );
\r
3509 if (piece != EmptySquare) {
\r
3510 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3514 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3516 oldBrush = SelectObject(hdc, brush );
\r
3517 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3518 SelectObject(hdc, oldBrush);
\r
3519 if (piece != EmptySquare)
\r
3520 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3525 if( texture_hdc != NULL ) {
\r
3526 DeleteDC( texture_hdc );
\r
3530 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3531 void fputDW(FILE *f, int x)
\r
3533 fputc(x & 255, f);
\r
3534 fputc(x>>8 & 255, f);
\r
3535 fputc(x>>16 & 255, f);
\r
3536 fputc(x>>24 & 255, f);
\r
3539 #define MAX_CLIPS 200 /* more than enough */
\r
3542 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3544 // HBITMAP bufferBitmap;
\r
3549 int w = 100, h = 50;
\r
3551 if(logo == NULL) {
\r
3552 if(!logoHeight) return;
\r
3553 FillRect( hdc, &logoRect, whitePieceBrush );
\r
3555 // GetClientRect(hwndMain, &Rect);
\r
3556 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3557 // Rect.bottom-Rect.top+1);
\r
3558 tmphdc = CreateCompatibleDC(hdc);
\r
3559 hbm = SelectObject(tmphdc, logo);
\r
3560 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3564 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3565 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3566 SelectObject(tmphdc, hbm);
\r
3574 HDC hdc = GetDC(hwndMain);
\r
3575 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3576 if(appData.autoLogo) {
\r
3578 switch(gameMode) { // pick logos based on game mode
\r
3579 case IcsObserving:
\r
3580 whiteLogo = second.programLogo; // ICS logo
\r
3581 blackLogo = second.programLogo;
\r
3584 case IcsPlayingWhite:
\r
3585 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3586 blackLogo = second.programLogo; // ICS logo
\r
3588 case IcsPlayingBlack:
\r
3589 whiteLogo = second.programLogo; // ICS logo
\r
3590 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3592 case TwoMachinesPlay:
\r
3593 if(first.twoMachinesColor[0] == 'b') {
\r
3594 whiteLogo = second.programLogo;
\r
3595 blackLogo = first.programLogo;
\r
3598 case MachinePlaysWhite:
\r
3599 blackLogo = userLogo;
\r
3601 case MachinePlaysBlack:
\r
3602 whiteLogo = userLogo;
\r
3603 blackLogo = first.programLogo;
\r
3606 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3607 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3608 ReleaseDC(hwndMain, hdc);
\r
3613 UpdateLogos(int display)
\r
3614 { // called after loading new engine(s), in tourney or from menu
\r
3615 LoadLogo(&first, 0, FALSE);
\r
3616 LoadLogo(&second, 1, appData.icsActive);
\r
3617 InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos
\r
3618 if(display) DisplayLogos();
\r
3621 static HDC hdcSeek;
\r
3623 // [HGM] seekgraph
\r
3624 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3627 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3628 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3629 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3630 SelectObject( hdcSeek, hp );
\r
3633 // front-end wrapper for drawing functions to do rectangles
\r
3634 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3639 if (hdcSeek == NULL) {
\r
3640 hdcSeek = GetDC(hwndMain);
\r
3641 if (!appData.monoMode) {
\r
3642 SelectPalette(hdcSeek, hPal, FALSE);
\r
3643 RealizePalette(hdcSeek);
\r
3646 hp = SelectObject( hdcSeek, gridPen );
\r
3647 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3648 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3649 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3650 SelectObject( hdcSeek, hp );
\r
3653 // front-end wrapper for putting text in graph
\r
3654 void DrawSeekText(char *buf, int x, int y)
\r
3657 SetBkMode( hdcSeek, TRANSPARENT );
\r
3658 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3659 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3662 void DrawSeekDot(int x, int y, int color)
\r
3664 int square = color & 0x80;
\r
3665 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3666 color == 0 ? markerBrush[1] : color == 1 ? darkSquareBrush : explodeBrush);
\r
3669 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3670 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3672 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3673 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3674 SelectObject(hdcSeek, oldBrush);
\r
3677 void DrawSeekOpen()
\r
3681 void DrawSeekClose()
\r
3686 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3688 static Board lastReq[2], lastDrawn[2];
\r
3689 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3690 static int lastDrawnFlipView = 0;
\r
3691 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3692 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3695 HBITMAP bufferBitmap;
\r
3696 HBITMAP oldBitmap;
\r
3698 HRGN clips[MAX_CLIPS];
\r
3699 ChessSquare dragged_piece = EmptySquare;
\r
3700 int nr = twoBoards*partnerUp;
\r
3702 /* I'm undecided on this - this function figures out whether a full
\r
3703 * repaint is necessary on its own, so there's no real reason to have the
\r
3704 * caller tell it that. I think this can safely be set to FALSE - but
\r
3705 * if we trust the callers not to request full repaints unnessesarily, then
\r
3706 * we could skip some clipping work. In other words, only request a full
\r
3707 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3708 * gamestart and similar) --Hawk
\r
3710 Boolean fullrepaint = repaint;
\r
3712 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3714 if( DrawPositionNeedsFullRepaint() ) {
\r
3715 fullrepaint = TRUE;
\r
3718 if (board == NULL) {
\r
3719 if (!lastReqValid[nr]) {
\r
3722 board = lastReq[nr];
\r
3724 CopyBoard(lastReq[nr], board);
\r
3725 lastReqValid[nr] = 1;
\r
3728 if (doingSizing) {
\r
3732 if (IsIconic(hwndMain)) {
\r
3736 if (hdc == NULL) {
\r
3737 hdc = GetDC(hwndMain);
\r
3738 if (!appData.monoMode) {
\r
3739 SelectPalette(hdc, hPal, FALSE);
\r
3740 RealizePalette(hdc);
\r
3744 releaseDC = FALSE;
\r
3747 /* Create some work-DCs */
\r
3748 hdcmem = CreateCompatibleDC(hdc);
\r
3749 tmphdc = CreateCompatibleDC(hdc);
\r
3751 /* If dragging is in progress, we temporarely remove the piece */
\r
3752 /* [HGM] or temporarily decrease count if stacked */
\r
3753 /* !! Moved to before board compare !! */
\r
3754 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3755 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3756 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3757 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3758 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3760 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3761 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3762 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3764 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3767 /* Figure out which squares need updating by comparing the
\r
3768 * newest board with the last drawn board and checking if
\r
3769 * flipping has changed.
\r
3771 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3772 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3773 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3774 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3775 SquareToPos(row, column, &x, &y);
\r
3776 clips[num_clips++] =
\r
3777 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3781 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3782 for (i=0; i<2; i++) {
\r
3783 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3784 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3785 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3786 lastDrawnHighlight.sq[i].y >= 0) {
\r
3787 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3788 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3789 clips[num_clips++] =
\r
3790 CreateRectRgn(x - lineGap, y - lineGap,
\r
3791 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3793 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3794 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3795 clips[num_clips++] =
\r
3796 CreateRectRgn(x - lineGap, y - lineGap,
\r
3797 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3801 for (i=0; i<2; i++) {
\r
3802 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3803 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3804 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3805 lastDrawnPremove.sq[i].y >= 0) {
\r
3806 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3807 lastDrawnPremove.sq[i].x, &x, &y);
\r
3808 clips[num_clips++] =
\r
3809 CreateRectRgn(x - lineGap, y - lineGap,
\r
3810 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3812 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3813 premoveHighlightInfo.sq[i].y >= 0) {
\r
3814 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3815 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3816 clips[num_clips++] =
\r
3817 CreateRectRgn(x - lineGap, y - lineGap,
\r
3818 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3822 } else { // nr == 1
\r
3823 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3824 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3825 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3826 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3827 for (i=0; i<2; i++) {
\r
3828 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3829 partnerHighlightInfo.sq[i].y >= 0) {
\r
3830 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3831 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3832 clips[num_clips++] =
\r
3833 CreateRectRgn(x - lineGap, y - lineGap,
\r
3834 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3836 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3837 oldPartnerHighlight.sq[i].y >= 0) {
\r
3838 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3839 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3840 clips[num_clips++] =
\r
3841 CreateRectRgn(x - lineGap, y - lineGap,
\r
3842 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3847 fullrepaint = TRUE;
\r
3850 /* Create a buffer bitmap - this is the actual bitmap
\r
3851 * being written to. When all the work is done, we can
\r
3852 * copy it to the real DC (the screen). This avoids
\r
3853 * the problems with flickering.
\r
3855 GetClientRect(hwndMain, &Rect);
\r
3856 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3857 Rect.bottom-Rect.top+1);
\r
3858 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3859 if (!appData.monoMode) {
\r
3860 SelectPalette(hdcmem, hPal, FALSE);
\r
3863 /* Create clips for dragging */
\r
3864 if (!fullrepaint) {
\r
3865 if (dragInfo.from.x >= 0) {
\r
3866 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3867 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3869 if (dragInfo.start.x >= 0) {
\r
3870 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3871 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3873 if (dragInfo.pos.x >= 0) {
\r
3874 x = dragInfo.pos.x - squareSize / 2;
\r
3875 y = dragInfo.pos.y - squareSize / 2;
\r
3876 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3878 if (dragInfo.lastpos.x >= 0) {
\r
3879 x = dragInfo.lastpos.x - squareSize / 2;
\r
3880 y = dragInfo.lastpos.y - squareSize / 2;
\r
3881 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3885 /* Are we animating a move?
\r
3887 * - remove the piece from the board (temporarely)
\r
3888 * - calculate the clipping region
\r
3890 if (!fullrepaint) {
\r
3891 if (animInfo.piece != EmptySquare) {
\r
3892 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3893 x = boardRect.left + animInfo.lastpos.x;
\r
3894 y = boardRect.top + animInfo.lastpos.y;
\r
3895 x2 = boardRect.left + animInfo.pos.x;
\r
3896 y2 = boardRect.top + animInfo.pos.y;
\r
3897 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3898 /* Slight kludge. The real problem is that after AnimateMove is
\r
3899 done, the position on the screen does not match lastDrawn.
\r
3900 This currently causes trouble only on e.p. captures in
\r
3901 atomic, where the piece moves to an empty square and then
\r
3902 explodes. The old and new positions both had an empty square
\r
3903 at the destination, but animation has drawn a piece there and
\r
3904 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3905 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3909 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3910 if (num_clips == 0)
\r
3911 fullrepaint = TRUE;
\r
3913 /* Set clipping on the memory DC */
\r
3914 if (!fullrepaint) {
\r
3915 SelectClipRgn(hdcmem, clips[0]);
\r
3916 for (x = 1; x < num_clips; x++) {
\r
3917 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3918 abort(); // this should never ever happen!
\r
3922 /* Do all the drawing to the memory DC */
\r
3923 if(explodeInfo.radius) { // [HGM] atomic
\r
3925 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3926 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3927 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3928 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3929 x += squareSize/2;
\r
3930 y += squareSize/2;
\r
3931 if(!fullrepaint) {
\r
3932 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3933 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3935 DrawGridOnDC(hdcmem);
\r
3936 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3937 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3938 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3939 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3940 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3941 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3942 SelectObject(hdcmem, oldBrush);
\r
3944 if(border) DrawBackgroundOnDC(hdcmem);
\r
3945 DrawGridOnDC(hdcmem);
\r
3946 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3947 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3948 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3950 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3951 oldPartnerHighlight = partnerHighlightInfo;
\r
3953 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3955 if(nr == 0) // [HGM] dual: markers only on left board
\r
3956 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3957 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3958 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3959 HBRUSH oldBrush = SelectObject(hdcmem, markerBrush[marker[row][column]-1]);
\r
3960 SquareToPos(row, column, &x, &y);
\r
3961 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3962 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3963 SelectObject(hdcmem, oldBrush);
\r
3968 if( appData.highlightMoveWithArrow ) {
\r
3969 DrawArrowHighlight(hdcmem);
\r
3972 DrawCoordsOnDC(hdcmem);
\r
3974 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3975 /* to make sure lastDrawn contains what is actually drawn */
\r
3977 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3978 if (dragged_piece != EmptySquare) {
\r
3979 /* [HGM] or restack */
\r
3980 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3981 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3983 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3984 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3986 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3987 x = dragInfo.pos.x - squareSize / 2;
\r
3988 y = dragInfo.pos.y - squareSize / 2;
\r
3989 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
3990 ((int) dragInfo.piece < (int) BlackPawn),
\r
3991 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3994 /* Put the animated piece back into place and draw it */
\r
3995 if (animInfo.piece != EmptySquare) {
\r
3996 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3997 x = boardRect.left + animInfo.pos.x;
\r
3998 y = boardRect.top + animInfo.pos.y;
\r
3999 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
4000 ((int) animInfo.piece < (int) BlackPawn),
\r
4001 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
4004 /* Release the bufferBitmap by selecting in the old bitmap
\r
4005 * and delete the memory DC
\r
4007 SelectObject(hdcmem, oldBitmap);
\r
4010 /* Set clipping on the target DC */
\r
4011 if (!fullrepaint) {
\r
4012 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
4014 GetRgnBox(clips[x], &rect);
\r
4015 DeleteObject(clips[x]);
\r
4016 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
4017 rect.right + wpMain.width/2, rect.bottom);
\r
4019 SelectClipRgn(hdc, clips[0]);
\r
4020 for (x = 1; x < num_clips; x++) {
\r
4021 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
4022 abort(); // this should never ever happen!
\r
4026 /* Copy the new bitmap onto the screen in one go.
\r
4027 * This way we avoid any flickering
\r
4029 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
4030 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
4031 boardRect.right - boardRect.left,
\r
4032 boardRect.bottom - boardRect.top,
\r
4033 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
4034 if(saveDiagFlag) {
\r
4035 BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData;
\r
4036 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
4038 GetObject(bufferBitmap, sizeof(b), &b);
\r
4039 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\r
4040 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
4041 bih.biWidth = b.bmWidth;
\r
4042 bih.biHeight = b.bmHeight;
\r
4044 bih.biBitCount = b.bmBitsPixel;
\r
4045 bih.biCompression = 0;
\r
4046 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
4047 bih.biXPelsPerMeter = 0;
\r
4048 bih.biYPelsPerMeter = 0;
\r
4049 bih.biClrUsed = 0;
\r
4050 bih.biClrImportant = 0;
\r
4051 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
4052 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
4053 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
4054 // fprintf(diagFile, "%8x\n", (int) pData);
\r
4056 wb = b.bmWidthBytes;
\r
4058 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
4059 int k = ((int*) pData)[i];
\r
4060 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4061 if(j >= 16) break;
\r
4063 if(j >= nrColors) nrColors = j+1;
\r
4065 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
4067 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
4068 for(w=0; w<(wb>>2); w+=2) {
\r
4069 int k = ((int*) pData)[(wb*i>>2) + w];
\r
4070 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4071 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
4072 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
4073 pData[p++] = m | j<<4;
\r
4075 while(p&3) pData[p++] = 0;
\r
4078 wb = ((wb+31)>>5)<<2;
\r
4080 // write BITMAPFILEHEADER
\r
4081 fprintf(diagFile, "BM");
\r
4082 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
4083 fputDW(diagFile, 0);
\r
4084 fputDW(diagFile, 0x36 + (fac?64:0));
\r
4085 // write BITMAPINFOHEADER
\r
4086 fputDW(diagFile, 40);
\r
4087 fputDW(diagFile, b.bmWidth);
\r
4088 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
4089 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
4090 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
4091 fputDW(diagFile, 0);
\r
4092 fputDW(diagFile, 0);
\r
4093 fputDW(diagFile, 0);
\r
4094 fputDW(diagFile, 0);
\r
4095 fputDW(diagFile, 0);
\r
4096 fputDW(diagFile, 0);
\r
4097 // write color table
\r
4099 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
4100 // write bitmap data
\r
4101 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
4102 fputc(pData[i], diagFile);
\r
4107 SelectObject(tmphdc, oldBitmap);
\r
4109 /* Massive cleanup */
\r
4110 for (x = 0; x < num_clips; x++)
\r
4111 DeleteObject(clips[x]);
\r
4114 DeleteObject(bufferBitmap);
\r
4117 ReleaseDC(hwndMain, hdc);
\r
4119 if (lastDrawnFlipView != flipView && nr == 0) {
\r
4121 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
4123 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
4126 /* CopyBoard(lastDrawn, board);*/
\r
4127 lastDrawnHighlight = highlightInfo;
\r
4128 lastDrawnPremove = premoveHighlightInfo;
\r
4129 lastDrawnFlipView = flipView;
\r
4130 lastDrawnValid[nr] = 1;
\r
4133 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
4138 saveDiagFlag = 1; diagFile = f;
\r
4139 HDCDrawPosition(NULL, TRUE, NULL);
\r
4147 /*---------------------------------------------------------------------------*\
\r
4148 | CLIENT PAINT PROCEDURE
\r
4149 | This is the main event-handler for the WM_PAINT message.
\r
4151 \*---------------------------------------------------------------------------*/
\r
4153 PaintProc(HWND hwnd)
\r
4159 if((hdc = BeginPaint(hwnd, &ps))) {
\r
4160 if (IsIconic(hwnd)) {
\r
4161 DrawIcon(hdc, 2, 2, iconCurrent);
\r
4163 if (!appData.monoMode) {
\r
4164 SelectPalette(hdc, hPal, FALSE);
\r
4165 RealizePalette(hdc);
\r
4167 HDCDrawPosition(hdc, 1, NULL);
\r
4168 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
4169 flipView = !flipView; partnerUp = !partnerUp;
\r
4170 HDCDrawPosition(hdc, 1, NULL);
\r
4171 flipView = !flipView; partnerUp = !partnerUp;
\r
4174 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
4175 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
4176 ETO_CLIPPED|ETO_OPAQUE,
\r
4177 &messageRect, messageText, strlen(messageText), NULL);
\r
4178 SelectObject(hdc, oldFont);
\r
4179 DisplayBothClocks();
\r
4182 EndPaint(hwnd,&ps);
\r
4190 * If the user selects on a border boundary, return -1; if off the board,
\r
4191 * return -2. Otherwise map the event coordinate to the square.
\r
4192 * The offset boardRect.left or boardRect.top must already have been
\r
4193 * subtracted from x.
\r
4195 int EventToSquare(x, limit)
\r
4200 if (x < lineGap + border)
\r
4202 x -= lineGap + border;
\r
4203 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4205 x /= (squareSize + lineGap);
\r
4217 DropEnable dropEnables[] = {
\r
4218 { 'P', DP_Pawn, N_("Pawn") },
\r
4219 { 'N', DP_Knight, N_("Knight") },
\r
4220 { 'B', DP_Bishop, N_("Bishop") },
\r
4221 { 'R', DP_Rook, N_("Rook") },
\r
4222 { 'Q', DP_Queen, N_("Queen") },
\r
4226 SetupDropMenu(HMENU hmenu)
\r
4228 int i, count, enable;
\r
4230 extern char white_holding[], black_holding[];
\r
4231 char item[MSG_SIZ];
\r
4233 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4234 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4235 dropEnables[i].piece);
\r
4237 while (p && *p++ == dropEnables[i].piece) count++;
\r
4238 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4239 enable = count > 0 || !appData.testLegality
\r
4240 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4241 && !appData.icsActive);
\r
4242 ModifyMenu(hmenu, dropEnables[i].command,
\r
4243 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4244 dropEnables[i].command, item);
\r
4248 void DragPieceBegin(int x, int y, Boolean instantly)
\r
4250 dragInfo.lastpos.x = boardRect.left + x;
\r
4251 dragInfo.lastpos.y = boardRect.top + y;
\r
4252 if(instantly) dragInfo.pos = dragInfo.lastpos;
\r
4253 dragInfo.from.x = fromX;
\r
4254 dragInfo.from.y = fromY;
\r
4255 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4256 dragInfo.start = dragInfo.from;
\r
4257 SetCapture(hwndMain);
\r
4260 void DragPieceEnd(int x, int y)
\r
4263 dragInfo.start.x = dragInfo.start.y = -1;
\r
4264 dragInfo.from = dragInfo.start;
\r
4265 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4268 void ChangeDragPiece(ChessSquare piece)
\r
4270 dragInfo.piece = piece;
\r
4273 /* Event handler for mouse messages */
\r
4275 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4279 static int recursive = 0;
\r
4281 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4284 if (message == WM_MBUTTONUP) {
\r
4285 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4286 to the middle button: we simulate pressing the left button too!
\r
4288 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4289 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4295 pt.x = LOWORD(lParam);
\r
4296 pt.y = HIWORD(lParam);
\r
4297 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4298 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4299 if (!flipView && y >= 0) {
\r
4300 y = BOARD_HEIGHT - 1 - y;
\r
4302 if (flipView && x >= 0) {
\r
4303 x = BOARD_WIDTH - 1 - x;
\r
4306 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4307 controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status
\r
4309 switch (message) {
\r
4310 case WM_LBUTTONDOWN:
\r
4311 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4312 ClockClick(flipClock); break;
\r
4313 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4314 ClockClick(!flipClock); break;
\r
4316 if(dragging) { // [HGM] lion: don't destroy dragging info if we are already dragging
\r
4317 dragInfo.start.x = dragInfo.start.y = -1;
\r
4318 dragInfo.from = dragInfo.start;
\r
4320 if(fromX == -1 && frozen) { // not sure where this is for
\r
4321 fromX = fromY = -1;
\r
4322 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4325 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4326 DrawPosition(TRUE, NULL);
\r
4329 case WM_LBUTTONUP:
\r
4330 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4331 DrawPosition(TRUE, NULL);
\r
4334 case WM_MOUSEMOVE:
\r
4335 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4336 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4337 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4338 if ((appData.animateDragging || appData.highlightDragging)
\r
4339 && (wParam & MK_LBUTTON || dragging == 2)
\r
4340 && dragInfo.from.x >= 0)
\r
4342 BOOL full_repaint = FALSE;
\r
4344 if (appData.animateDragging) {
\r
4345 dragInfo.pos = pt;
\r
4347 if (appData.highlightDragging) {
\r
4348 HoverEvent(highlightInfo.sq[1].x, highlightInfo.sq[1].y, x, y);
\r
4349 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4350 full_repaint = TRUE;
\r
4354 DrawPosition( full_repaint, NULL);
\r
4356 dragInfo.lastpos = dragInfo.pos;
\r
4360 case WM_MOUSEWHEEL: // [DM]
\r
4361 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4362 /* Mouse Wheel is being rolled forward
\r
4363 * Play moves forward
\r
4365 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4366 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4367 /* Mouse Wheel is being rolled backward
\r
4368 * Play moves backward
\r
4370 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4371 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4375 case WM_MBUTTONUP:
\r
4376 case WM_RBUTTONUP:
\r
4378 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4381 case WM_MBUTTONDOWN:
\r
4382 case WM_RBUTTONDOWN:
\r
4385 fromX = fromY = -1;
\r
4386 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4387 dragInfo.start.x = dragInfo.start.y = -1;
\r
4388 dragInfo.from = dragInfo.start;
\r
4389 dragInfo.lastpos = dragInfo.pos;
\r
4390 if (appData.highlightDragging) {
\r
4391 ClearHighlights();
\r
4394 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4395 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4396 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4397 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4398 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4402 DrawPosition(TRUE, NULL);
\r
4404 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4407 if (message == WM_MBUTTONDOWN) {
\r
4408 buttonCount = 3; /* even if system didn't think so */
\r
4409 if (wParam & MK_SHIFT)
\r
4410 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4412 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4413 } else { /* message == WM_RBUTTONDOWN */
\r
4414 /* Just have one menu, on the right button. Windows users don't
\r
4415 think to try the middle one, and sometimes other software steals
\r
4416 it, or it doesn't really exist. */
\r
4417 if(gameInfo.variant != VariantShogi)
\r
4418 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4420 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4424 SetCapture(hwndMain);
\r
4427 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4428 SetupDropMenu(hmenu);
\r
4429 MenuPopup(hwnd, pt, hmenu, -1);
\r
4439 /* Preprocess messages for buttons in main window */
\r
4441 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4443 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4446 for (i=0; i<N_BUTTONS; i++) {
\r
4447 if (buttonDesc[i].id == id) break;
\r
4449 if (i == N_BUTTONS) return 0;
\r
4450 switch (message) {
\r
4455 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4456 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4463 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4466 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4467 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4468 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4469 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4471 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4473 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4474 TypeInEvent((char)wParam);
\r
4480 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4483 static int promoStyle;
\r
4485 /* Process messages for Promotion dialog box */
\r
4487 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4491 switch (message) {
\r
4492 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4493 /* Center the dialog over the application window */
\r
4494 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4495 Translate(hDlg, DLG_PromotionKing);
\r
4496 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4497 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4498 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4499 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4500 SW_SHOW : SW_HIDE);
\r
4501 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4502 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4503 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4504 PieceToChar(WhiteAngel) != '~') ||
\r
4505 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4506 PieceToChar(BlackAngel) != '~') ) ?
\r
4507 SW_SHOW : SW_HIDE);
\r
4508 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4509 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4510 PieceToChar(WhiteMarshall) != '~') ||
\r
4511 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4512 PieceToChar(BlackMarshall) != '~') ) ?
\r
4513 SW_SHOW : SW_HIDE);
\r
4514 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4515 ShowWindow(GetDlgItem(hDlg, PB_Rook), !promoStyle ? SW_SHOW : SW_HIDE);
\r
4516 ShowWindow(GetDlgItem(hDlg, PB_Bishop), !promoStyle ? SW_SHOW : SW_HIDE);
\r
4518 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4519 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4520 SetWindowText(hDlg, "Promote?");
\r
4522 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4523 gameInfo.variant == VariantSuper ?
\r
4524 SW_SHOW : SW_HIDE);
\r
4527 case WM_COMMAND: /* message: received a command */
\r
4528 switch (LOWORD(wParam)) {
\r
4530 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4531 ClearHighlights();
\r
4532 DrawPosition(FALSE, NULL);
\r
4535 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4538 promoChar = promoStyle ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4541 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4542 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4545 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4546 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4548 case PB_Chancellor:
\r
4549 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4551 case PB_Archbishop:
\r
4552 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4555 promoChar = gameInfo.variant == VariantShogi ? '=' : promoStyle ? NULLCHAR :
\r
4556 ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight));
\r
4561 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4562 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4563 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4564 fromX = fromY = -1;
\r
4565 if (!appData.highlightLastMove) {
\r
4566 ClearHighlights();
\r
4567 DrawPosition(FALSE, NULL);
\r
4574 /* Pop up promotion dialog */
\r
4576 PromotionPopup(HWND hwnd)
\r
4580 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4581 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4582 hwnd, (DLGPROC)lpProc);
\r
4583 FreeProcInstance(lpProc);
\r
4587 PromotionPopUp(char choice)
\r
4589 promoStyle = (choice == '+');
\r
4590 DrawPosition(TRUE, NULL);
\r
4591 PromotionPopup(hwndMain);
\r
4595 LoadGameDialog(HWND hwnd, char* title)
\r
4599 char fileTitle[MSG_SIZ];
\r
4600 f = OpenFileDialog(hwnd, "rb", "",
\r
4601 appData.oldSaveStyle ? "gam" : "pgn",
\r
4603 title, &number, fileTitle, NULL);
\r
4605 cmailMsgLoaded = FALSE;
\r
4606 if (number == 0) {
\r
4607 int error = GameListBuild(f);
\r
4609 DisplayError(_("Cannot build game list"), error);
\r
4610 } else if (!ListEmpty(&gameList) &&
\r
4611 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4612 GameListPopUp(f, fileTitle);
\r
4615 GameListDestroy();
\r
4618 LoadGame(f, number, fileTitle, FALSE);
\r
4622 int get_term_width()
\r
4627 HFONT hfont, hold_font;
\r
4632 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4636 // get the text metrics
\r
4637 hdc = GetDC(hText);
\r
4638 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4639 if (consoleCF.dwEffects & CFE_BOLD)
\r
4640 lf.lfWeight = FW_BOLD;
\r
4641 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4642 lf.lfItalic = TRUE;
\r
4643 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4644 lf.lfStrikeOut = TRUE;
\r
4645 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4646 lf.lfUnderline = TRUE;
\r
4647 hfont = CreateFontIndirect(&lf);
\r
4648 hold_font = SelectObject(hdc, hfont);
\r
4649 GetTextMetrics(hdc, &tm);
\r
4650 SelectObject(hdc, hold_font);
\r
4651 DeleteObject(hfont);
\r
4652 ReleaseDC(hText, hdc);
\r
4654 // get the rectangle
\r
4655 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4657 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4660 void UpdateICSWidth(HWND hText)
\r
4662 LONG old_width, new_width;
\r
4664 new_width = get_term_width(hText, FALSE);
\r
4665 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4666 if (new_width != old_width)
\r
4668 ics_update_width(new_width);
\r
4669 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4674 ChangedConsoleFont()
\r
4677 CHARRANGE tmpsel, sel;
\r
4678 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4679 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4680 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4683 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4684 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4685 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4686 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4687 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4688 * size. This was undocumented in the version of MSVC++ that I had
\r
4689 * when I wrote the code, but is apparently documented now.
\r
4691 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4692 cfmt.bCharSet = f->lf.lfCharSet;
\r
4693 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4694 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4695 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4696 /* Why are the following seemingly needed too? */
\r
4697 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4698 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4699 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4701 tmpsel.cpMax = -1; /*999999?*/
\r
4702 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4703 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4704 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4705 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4707 paraf.cbSize = sizeof(paraf);
\r
4708 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4709 paraf.dxStartIndent = 0;
\r
4710 paraf.dxOffset = WRAP_INDENT;
\r
4711 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4712 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4713 UpdateICSWidth(hText);
\r
4716 /*---------------------------------------------------------------------------*\
\r
4718 * Window Proc for main window
\r
4720 \*---------------------------------------------------------------------------*/
\r
4722 /* Process messages for main window, etc. */
\r
4724 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4731 char fileTitle[MSG_SIZ];
\r
4732 static SnapData sd;
\r
4733 static int peek=0;
\r
4735 switch (message) {
\r
4737 case WM_PAINT: /* message: repaint portion of window */
\r
4741 case WM_ERASEBKGND:
\r
4742 if (IsIconic(hwnd)) {
\r
4743 /* Cheat; change the message */
\r
4744 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4746 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4750 case WM_LBUTTONDOWN:
\r
4751 case WM_MBUTTONDOWN:
\r
4752 case WM_RBUTTONDOWN:
\r
4753 case WM_LBUTTONUP:
\r
4754 case WM_MBUTTONUP:
\r
4755 case WM_RBUTTONUP:
\r
4756 case WM_MOUSEMOVE:
\r
4757 case WM_MOUSEWHEEL:
\r
4758 MouseEvent(hwnd, message, wParam, lParam);
\r
4762 if((char)wParam == '\b') {
\r
4763 ForwardEvent(); peek = 0;
\r
4766 JAWS_KBUP_NAVIGATION
\r
4771 if((char)wParam == '\b') {
\r
4772 if(!peek) BackwardEvent(), peek = 1;
\r
4775 JAWS_KBDOWN_NAVIGATION
\r
4781 JAWS_ALT_INTERCEPT
\r
4783 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4784 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4785 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4786 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4788 SendMessage(h, message, wParam, lParam);
\r
4789 } else if(lParam != KF_REPEAT) {
\r
4790 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4791 TypeInEvent((char)wParam);
\r
4792 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4793 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4798 case WM_PALETTECHANGED:
\r
4799 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4801 HDC hdc = GetDC(hwndMain);
\r
4802 SelectPalette(hdc, hPal, TRUE);
\r
4803 nnew = RealizePalette(hdc);
\r
4805 paletteChanged = TRUE;
\r
4807 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4809 ReleaseDC(hwnd, hdc);
\r
4813 case WM_QUERYNEWPALETTE:
\r
4814 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4816 HDC hdc = GetDC(hwndMain);
\r
4817 paletteChanged = FALSE;
\r
4818 SelectPalette(hdc, hPal, FALSE);
\r
4819 nnew = RealizePalette(hdc);
\r
4821 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4823 ReleaseDC(hwnd, hdc);
\r
4828 case WM_COMMAND: /* message: command from application menu */
\r
4829 wmId = LOWORD(wParam);
\r
4834 SAY("new game enter a move to play against the computer with white");
\r
4837 case IDM_NewGameFRC:
\r
4838 if( NewGameFRC() == 0 ) {
\r
4843 case IDM_NewVariant:
\r
4844 NewVariantPopup(hwnd);
\r
4847 case IDM_LoadGame:
\r
4848 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4851 case IDM_LoadNextGame:
\r
4855 case IDM_LoadPrevGame:
\r
4859 case IDM_ReloadGame:
\r
4863 case IDM_LoadPosition:
\r
4864 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4865 Reset(FALSE, TRUE);
\r
4868 f = OpenFileDialog(hwnd, "rb", "",
\r
4869 appData.oldSaveStyle ? "pos" : "fen",
\r
4871 _("Load Position from File"), &number, fileTitle, NULL);
\r
4873 LoadPosition(f, number, fileTitle);
\r
4877 case IDM_LoadNextPosition:
\r
4878 ReloadPosition(1);
\r
4881 case IDM_LoadPrevPosition:
\r
4882 ReloadPosition(-1);
\r
4885 case IDM_ReloadPosition:
\r
4886 ReloadPosition(0);
\r
4889 case IDM_SaveGame:
\r
4890 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4891 f = OpenFileDialog(hwnd, "a", defName,
\r
4892 appData.oldSaveStyle ? "gam" : "pgn",
\r
4894 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4896 SaveGame(f, 0, "");
\r
4900 case IDM_SavePosition:
\r
4901 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4902 f = OpenFileDialog(hwnd, "a", defName,
\r
4903 appData.oldSaveStyle ? "pos" : "fen",
\r
4905 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4907 SavePosition(f, 0, "");
\r
4911 case IDM_SaveDiagram:
\r
4912 defName = "diagram";
\r
4913 f = OpenFileDialog(hwnd, "wb", defName,
\r
4916 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
4922 case IDM_SaveSelected:
\r
4923 f = OpenFileDialog(hwnd, "a", "",
\r
4926 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4928 SaveSelected(f, 0, "");
\r
4932 case IDM_CreateBook:
\r
4933 CreateBookEvent();
\r
4936 case IDM_CopyGame:
\r
4937 CopyGameToClipboard();
\r
4940 case IDM_PasteGame:
\r
4941 PasteGameFromClipboard();
\r
4944 case IDM_CopyGameListToClipboard:
\r
4945 CopyGameListToClipboard();
\r
4948 /* [AS] Autodetect FEN or PGN data */
\r
4949 case IDM_PasteAny:
\r
4950 PasteGameOrFENFromClipboard();
\r
4953 /* [AS] Move history */
\r
4954 case IDM_ShowMoveHistory:
\r
4955 if( MoveHistoryIsUp() ) {
\r
4956 MoveHistoryPopDown();
\r
4959 MoveHistoryPopUp();
\r
4963 /* [AS] Eval graph */
\r
4964 case IDM_ShowEvalGraph:
\r
4965 if( EvalGraphIsUp() ) {
\r
4966 EvalGraphPopDown();
\r
4970 SetFocus(hwndMain);
\r
4974 /* [AS] Engine output */
\r
4975 case IDM_ShowEngineOutput:
\r
4976 if( EngineOutputIsUp() ) {
\r
4977 EngineOutputPopDown();
\r
4980 EngineOutputPopUp();
\r
4984 /* [AS] User adjudication */
\r
4985 case IDM_UserAdjudication_White:
\r
4986 UserAdjudicationEvent( +1 );
\r
4989 case IDM_UserAdjudication_Black:
\r
4990 UserAdjudicationEvent( -1 );
\r
4993 case IDM_UserAdjudication_Draw:
\r
4994 UserAdjudicationEvent( 0 );
\r
4997 /* [AS] Game list options dialog */
\r
4998 case IDM_GameListOptions:
\r
4999 GameListOptions();
\r
5006 case IDM_CopyPosition:
\r
5007 CopyFENToClipboard();
\r
5010 case IDM_PastePosition:
\r
5011 PasteFENFromClipboard();
\r
5014 case IDM_MailMove:
\r
5018 case IDM_ReloadCMailMsg:
\r
5019 Reset(TRUE, TRUE);
\r
5020 ReloadCmailMsgEvent(FALSE);
\r
5023 case IDM_Minimize:
\r
5024 ShowWindow(hwnd, SW_MINIMIZE);
\r
5031 case IDM_MachineWhite:
\r
5032 MachineWhiteEvent();
\r
5034 * refresh the tags dialog only if it's visible
\r
5036 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
5038 tags = PGNTags(&gameInfo);
\r
5039 TagsPopUp(tags, CmailMsg());
\r
5042 SAY("computer starts playing white");
\r
5045 case IDM_MachineBlack:
\r
5046 MachineBlackEvent();
\r
5048 * refresh the tags dialog only if it's visible
\r
5050 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
5052 tags = PGNTags(&gameInfo);
\r
5053 TagsPopUp(tags, CmailMsg());
\r
5056 SAY("computer starts playing black");
\r
5059 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
5060 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
5063 case IDM_TwoMachines:
\r
5064 TwoMachinesEvent();
\r
5066 * refresh the tags dialog only if it's visible
\r
5068 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
5070 tags = PGNTags(&gameInfo);
\r
5071 TagsPopUp(tags, CmailMsg());
\r
5074 SAY("computer starts playing both sides");
\r
5077 case IDM_AnalysisMode:
\r
5078 if(AnalyzeModeEvent()) {
\r
5079 SAY("analyzing current position");
\r
5083 case IDM_AnalyzeFile:
\r
5084 AnalyzeFileEvent();
\r
5087 case IDM_IcsClient:
\r
5091 case IDM_EditGame:
\r
5092 case IDM_EditGame2:
\r
5097 case IDM_EditPosition:
\r
5098 case IDM_EditPosition2:
\r
5099 EditPositionEvent();
\r
5100 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
5103 case IDM_Training:
\r
5107 case IDM_ShowGameList:
\r
5108 ShowGameListProc();
\r
5111 case IDM_EditProgs1:
\r
5112 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
5115 case IDM_LoadProg1:
\r
5116 LoadEnginePopUp(hwndMain, 0);
\r
5119 case IDM_LoadProg2:
\r
5120 LoadEnginePopUp(hwndMain, 1);
\r
5123 case IDM_EditServers:
\r
5124 EditTagsPopUp(icsNames, &icsNames);
\r
5127 case IDM_EditTags:
\r
5132 case IDM_EditBook:
\r
5136 case IDM_EditComment:
\r
5138 if (commentUp && editComment) {
\r
5141 EditCommentEvent();
\r
5162 case IDM_CallFlag:
\r
5182 case IDM_StopObserving:
\r
5183 StopObservingEvent();
\r
5186 case IDM_StopExamining:
\r
5187 StopExaminingEvent();
\r
5191 UploadGameEvent();
\r
5194 case IDM_TypeInMove:
\r
5195 TypeInEvent('\000');
\r
5198 case IDM_TypeInName:
\r
5199 PopUpNameDialog('\000');
\r
5202 case IDM_Backward:
\r
5204 SetFocus(hwndMain);
\r
5211 SetFocus(hwndMain);
\r
5216 SetFocus(hwndMain);
\r
5221 SetFocus(hwndMain);
\r
5224 case OPT_GameListNext: // [HGM] forward these two accelerators to Game List
\r
5225 case OPT_GameListPrev:
\r
5226 if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);
\r
5230 RevertEvent(FALSE);
\r
5233 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5234 RevertEvent(TRUE);
\r
5237 case IDM_TruncateGame:
\r
5238 TruncateGameEvent();
\r
5245 case IDM_RetractMove:
\r
5246 RetractMoveEvent();
\r
5249 case IDM_FlipView:
\r
5250 flipView = !flipView;
\r
5251 DrawPosition(FALSE, NULL);
\r
5254 case IDM_FlipClock:
\r
5255 flipClock = !flipClock;
\r
5256 DisplayBothClocks();
\r
5260 case IDM_MuteSounds:
\r
5261 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5262 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5263 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5266 case IDM_GeneralOptions:
\r
5267 GeneralOptionsPopup(hwnd);
\r
5268 DrawPosition(TRUE, NULL);
\r
5271 case IDM_BoardOptions:
\r
5272 BoardOptionsPopup(hwnd);
\r
5275 case IDM_ThemeOptions:
\r
5276 ThemeOptionsPopup(hwnd);
\r
5279 case IDM_EnginePlayOptions:
\r
5280 EnginePlayOptionsPopup(hwnd);
\r
5283 case IDM_Engine1Options:
\r
5284 EngineOptionsPopup(hwnd, &first);
\r
5287 case IDM_Engine2Options:
\r
5289 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5290 EngineOptionsPopup(hwnd, &second);
\r
5293 case IDM_OptionsUCI:
\r
5294 UciOptionsPopup(hwnd);
\r
5298 TourneyPopup(hwnd);
\r
5301 case IDM_IcsOptions:
\r
5302 IcsOptionsPopup(hwnd);
\r
5306 FontsOptionsPopup(hwnd);
\r
5310 SoundOptionsPopup(hwnd);
\r
5313 case IDM_CommPort:
\r
5314 CommPortOptionsPopup(hwnd);
\r
5317 case IDM_LoadOptions:
\r
5318 LoadOptionsPopup(hwnd);
\r
5321 case IDM_SaveOptions:
\r
5322 SaveOptionsPopup(hwnd);
\r
5325 case IDM_TimeControl:
\r
5326 TimeControlOptionsPopup(hwnd);
\r
5329 case IDM_SaveSettings:
\r
5330 SaveSettings(settingsFileName);
\r
5333 case IDM_SaveSettingsOnExit:
\r
5334 saveSettingsOnExit = !saveSettingsOnExit;
\r
5335 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5336 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5337 MF_CHECKED : MF_UNCHECKED));
\r
5348 case IDM_AboutGame:
\r
5353 appData.debugMode = !appData.debugMode;
\r
5354 if (appData.debugMode) {
\r
5355 char dir[MSG_SIZ];
\r
5356 GetCurrentDirectory(MSG_SIZ, dir);
\r
5357 SetCurrentDirectory(installDir);
\r
5358 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5359 SetCurrentDirectory(dir);
\r
5360 setbuf(debugFP, NULL);
\r
5367 case IDM_HELPCONTENTS:
\r
5368 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5369 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5370 MessageBox (GetFocus(),
\r
5371 _("Unable to activate help"),
\r
5372 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5376 case IDM_HELPSEARCH:
\r
5377 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5378 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5379 MessageBox (GetFocus(),
\r
5380 _("Unable to activate help"),
\r
5381 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5385 case IDM_HELPHELP:
\r
5386 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5387 MessageBox (GetFocus(),
\r
5388 _("Unable to activate help"),
\r
5389 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5394 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5396 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5397 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5398 FreeProcInstance(lpProc);
\r
5401 case IDM_DirectCommand1:
\r
5402 AskQuestionEvent(_("Direct Command"),
\r
5403 _("Send to chess program:"), "", "1");
\r
5405 case IDM_DirectCommand2:
\r
5406 AskQuestionEvent(_("Direct Command"),
\r
5407 _("Send to second chess program:"), "", "2");
\r
5410 case EP_WhitePawn:
\r
5411 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5412 fromX = fromY = -1;
\r
5415 case EP_WhiteKnight:
\r
5416 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5417 fromX = fromY = -1;
\r
5420 case EP_WhiteBishop:
\r
5421 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5422 fromX = fromY = -1;
\r
5425 case EP_WhiteRook:
\r
5426 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5427 fromX = fromY = -1;
\r
5430 case EP_WhiteQueen:
\r
5431 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5432 fromX = fromY = -1;
\r
5435 case EP_WhiteFerz:
\r
5436 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5437 fromX = fromY = -1;
\r
5440 case EP_WhiteWazir:
\r
5441 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5442 fromX = fromY = -1;
\r
5445 case EP_WhiteAlfil:
\r
5446 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5447 fromX = fromY = -1;
\r
5450 case EP_WhiteCannon:
\r
5451 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5452 fromX = fromY = -1;
\r
5455 case EP_WhiteCardinal:
\r
5456 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5457 fromX = fromY = -1;
\r
5460 case EP_WhiteMarshall:
\r
5461 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5462 fromX = fromY = -1;
\r
5465 case EP_WhiteKing:
\r
5466 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5467 fromX = fromY = -1;
\r
5470 case EP_BlackPawn:
\r
5471 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5472 fromX = fromY = -1;
\r
5475 case EP_BlackKnight:
\r
5476 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5477 fromX = fromY = -1;
\r
5480 case EP_BlackBishop:
\r
5481 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5482 fromX = fromY = -1;
\r
5485 case EP_BlackRook:
\r
5486 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5487 fromX = fromY = -1;
\r
5490 case EP_BlackQueen:
\r
5491 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5492 fromX = fromY = -1;
\r
5495 case EP_BlackFerz:
\r
5496 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5497 fromX = fromY = -1;
\r
5500 case EP_BlackWazir:
\r
5501 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5502 fromX = fromY = -1;
\r
5505 case EP_BlackAlfil:
\r
5506 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5507 fromX = fromY = -1;
\r
5510 case EP_BlackCannon:
\r
5511 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5512 fromX = fromY = -1;
\r
5515 case EP_BlackCardinal:
\r
5516 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5517 fromX = fromY = -1;
\r
5520 case EP_BlackMarshall:
\r
5521 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5522 fromX = fromY = -1;
\r
5525 case EP_BlackKing:
\r
5526 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5527 fromX = fromY = -1;
\r
5530 case EP_EmptySquare:
\r
5531 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5532 fromX = fromY = -1;
\r
5535 case EP_ClearBoard:
\r
5536 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5537 fromX = fromY = -1;
\r
5541 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5542 fromX = fromY = -1;
\r
5546 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5547 fromX = fromY = -1;
\r
5551 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5552 fromX = fromY = -1;
\r
5556 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5557 fromX = fromY = -1;
\r
5561 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5562 fromX = fromY = -1;
\r
5566 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5567 fromX = fromY = -1;
\r
5571 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5572 fromX = fromY = -1;
\r
5576 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5577 fromX = fromY = -1;
\r
5581 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5582 fromX = fromY = -1;
\r
5586 barbaric = 0; appData.language = "";
\r
5587 TranslateMenus(0);
\r
5588 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5589 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5590 lastChecked = wmId;
\r
5594 if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)
\r
5595 RecentEngineEvent(wmId - IDM_RecentEngines);
\r
5597 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5598 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5599 TranslateMenus(0);
\r
5600 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5601 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5602 lastChecked = wmId;
\r
5605 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5611 case CLOCK_TIMER_ID:
\r
5612 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5613 clockTimerEvent = 0;
\r
5614 DecrementClocks(); /* call into back end */
\r
5616 case LOAD_GAME_TIMER_ID:
\r
5617 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5618 loadGameTimerEvent = 0;
\r
5619 AutoPlayGameLoop(); /* call into back end */
\r
5621 case ANALYSIS_TIMER_ID:
\r
5622 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5623 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5624 AnalysisPeriodicEvent(0);
\r
5626 KillTimer(hwnd, analysisTimerEvent);
\r
5627 analysisTimerEvent = 0;
\r
5630 case DELAYED_TIMER_ID:
\r
5631 KillTimer(hwnd, delayedTimerEvent);
\r
5632 delayedTimerEvent = 0;
\r
5633 delayedTimerCallback();
\r
5638 case WM_USER_Input:
\r
5639 InputEvent(hwnd, message, wParam, lParam);
\r
5642 /* [AS] Also move "attached" child windows */
\r
5643 case WM_WINDOWPOSCHANGING:
\r
5645 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5646 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5648 if( ((lpwp->flags & SWP_NOMOVE) == 0) /*&& ((lpwp->flags & SWP_NOSIZE) != 0)*/ ) { // [HGM] in Win8 size always accompanies move?
\r
5649 /* Window is moving */
\r
5652 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5653 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5654 rcMain.right = wpMain.x + wpMain.width;
\r
5655 rcMain.top = wpMain.y;
\r
5656 rcMain.bottom = wpMain.y + wpMain.height;
\r
5658 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5659 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5660 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5661 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5662 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5663 wpMain.x = lpwp->x;
\r
5664 wpMain.y = lpwp->y;
\r
5669 /* [AS] Snapping */
\r
5670 case WM_ENTERSIZEMOVE:
\r
5671 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5672 if (hwnd == hwndMain) {
\r
5673 doingSizing = TRUE;
\r
5676 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5680 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5681 if (hwnd == hwndMain) {
\r
5682 lastSizing = wParam;
\r
5687 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5688 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5690 case WM_EXITSIZEMOVE:
\r
5691 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5692 if (hwnd == hwndMain) {
\r
5694 doingSizing = FALSE;
\r
5695 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5696 GetClientRect(hwnd, &client);
\r
5697 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5699 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5701 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5704 case WM_DESTROY: /* message: window being destroyed */
\r
5705 PostQuitMessage(0);
\r
5709 if (hwnd == hwndMain) {
\r
5714 default: /* Passes it on if unprocessed */
\r
5715 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5720 /*---------------------------------------------------------------------------*\
\r
5722 * Misc utility routines
\r
5724 \*---------------------------------------------------------------------------*/
\r
5727 * Decent random number generator, at least not as bad as Windows
\r
5728 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5730 unsigned int randstate;
\r
5735 randstate = randstate * 1664525 + 1013904223;
\r
5736 return (int) randstate & 0x7fffffff;
\r
5740 mysrandom(unsigned int seed)
\r
5747 * returns TRUE if user selects a different color, FALSE otherwise
\r
5751 ChangeColor(HWND hwnd, COLORREF *which)
\r
5753 static BOOL firstTime = TRUE;
\r
5754 static DWORD customColors[16];
\r
5756 COLORREF newcolor;
\r
5761 /* Make initial colors in use available as custom colors */
\r
5762 /* Should we put the compiled-in defaults here instead? */
\r
5764 customColors[i++] = lightSquareColor & 0xffffff;
\r
5765 customColors[i++] = darkSquareColor & 0xffffff;
\r
5766 customColors[i++] = whitePieceColor & 0xffffff;
\r
5767 customColors[i++] = blackPieceColor & 0xffffff;
\r
5768 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5769 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5771 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5772 customColors[i++] = textAttribs[ccl].color;
\r
5774 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5775 firstTime = FALSE;
\r
5778 cc.lStructSize = sizeof(cc);
\r
5779 cc.hwndOwner = hwnd;
\r
5780 cc.hInstance = NULL;
\r
5781 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5782 cc.lpCustColors = (LPDWORD) customColors;
\r
5783 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5785 if (!ChooseColor(&cc)) return FALSE;
\r
5787 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5788 if (newcolor == *which) return FALSE;
\r
5789 *which = newcolor;
\r
5793 InitDrawingColors();
\r
5794 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5799 MyLoadSound(MySound *ms)
\r
5805 if (ms->data && ms->flag) free(ms->data);
\r
5808 switch (ms->name[0]) {
\r
5814 /* System sound from Control Panel. Don't preload here. */
\r
5818 if (ms->name[1] == NULLCHAR) {
\r
5819 /* "!" alone = silence */
\r
5822 /* Builtin wave resource. Error if not found. */
\r
5823 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5824 if (h == NULL) break;
\r
5825 ms->data = (void *)LoadResource(hInst, h);
\r
5826 ms->flag = 0; // not maloced, so cannot be freed!
\r
5827 if (h == NULL) break;
\r
5832 /* .wav file. Error if not found. */
\r
5833 f = fopen(ms->name, "rb");
\r
5834 if (f == NULL) break;
\r
5835 if (fstat(fileno(f), &st) < 0) break;
\r
5836 ms->data = malloc(st.st_size);
\r
5838 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5844 char buf[MSG_SIZ];
\r
5845 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5846 DisplayError(buf, GetLastError());
\r
5852 MyPlaySound(MySound *ms)
\r
5854 BOOLEAN ok = FALSE;
\r
5856 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5857 switch (ms->name[0]) {
\r
5859 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5864 /* System sound from Control Panel (deprecated feature).
\r
5865 "$" alone or an unset sound name gets default beep (still in use). */
\r
5866 if (ms->name[1]) {
\r
5867 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5869 if (!ok) ok = MessageBeep(MB_OK);
\r
5872 /* Builtin wave resource, or "!" alone for silence */
\r
5873 if (ms->name[1]) {
\r
5874 if (ms->data == NULL) return FALSE;
\r
5875 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5881 /* .wav file. Error if not found. */
\r
5882 if (ms->data == NULL) return FALSE;
\r
5883 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5886 /* Don't print an error: this can happen innocently if the sound driver
\r
5887 is busy; for instance, if another instance of WinBoard is playing
\r
5888 a sound at about the same time. */
\r
5894 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5897 OPENFILENAME *ofn;
\r
5898 static UINT *number; /* gross that this is static */
\r
5900 switch (message) {
\r
5901 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5902 /* Center the dialog over the application window */
\r
5903 ofn = (OPENFILENAME *) lParam;
\r
5904 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5905 number = (UINT *) ofn->lCustData;
\r
5906 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5910 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5911 Translate(hDlg, 1536);
\r
5912 return FALSE; /* Allow for further processing */
\r
5915 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5916 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5918 return FALSE; /* Allow for further processing */
\r
5924 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5926 static UINT *number;
\r
5927 OPENFILENAME *ofname;
\r
5930 case WM_INITDIALOG:
\r
5931 Translate(hdlg, DLG_IndexNumber);
\r
5932 ofname = (OPENFILENAME *)lParam;
\r
5933 number = (UINT *)(ofname->lCustData);
\r
5936 ofnot = (OFNOTIFY *)lParam;
\r
5937 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5938 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5947 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5948 char *nameFilt, char *dlgTitle, UINT *number,
\r
5949 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5951 OPENFILENAME openFileName;
\r
5952 char buf1[MSG_SIZ];
\r
5955 if (fileName == NULL) fileName = buf1;
\r
5956 if (defName == NULL) {
\r
5957 safeStrCpy(fileName, "*.", 3 );
\r
5958 strcat(fileName, defExt);
\r
5960 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5962 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5963 if (number) *number = 0;
\r
5965 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5966 openFileName.hwndOwner = hwnd;
\r
5967 openFileName.hInstance = (HANDLE) hInst;
\r
5968 openFileName.lpstrFilter = nameFilt;
\r
5969 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5970 openFileName.nMaxCustFilter = 0L;
\r
5971 openFileName.nFilterIndex = 1L;
\r
5972 openFileName.lpstrFile = fileName;
\r
5973 openFileName.nMaxFile = MSG_SIZ;
\r
5974 openFileName.lpstrFileTitle = fileTitle;
\r
5975 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5976 openFileName.lpstrInitialDir = NULL;
\r
5977 openFileName.lpstrTitle = dlgTitle;
\r
5978 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5979 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5980 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5981 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5982 openFileName.nFileOffset = 0;
\r
5983 openFileName.nFileExtension = 0;
\r
5984 openFileName.lpstrDefExt = defExt;
\r
5985 openFileName.lCustData = (LONG) number;
\r
5986 openFileName.lpfnHook = oldDialog ?
\r
5987 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5988 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5990 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5991 GetOpenFileName(&openFileName)) {
\r
5992 /* open the file */
\r
5993 f = fopen(openFileName.lpstrFile, write);
\r
5995 MessageBox(hwnd, _("File open failed"), NULL,
\r
5996 MB_OK|MB_ICONEXCLAMATION);
\r
6000 int err = CommDlgExtendedError();
\r
6001 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
6010 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
6012 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
6015 * Get the first pop-up menu in the menu template. This is the
\r
6016 * menu that TrackPopupMenu displays.
\r
6018 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
6019 TranslateOneMenu(10, hmenuTrackPopup);
\r
6021 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
6024 * TrackPopup uses screen coordinates, so convert the
\r
6025 * coordinates of the mouse click to screen coordinates.
\r
6027 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
6029 /* Draw and track the floating pop-up menu. */
\r
6030 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
6031 pt.x, pt.y, 0, hwnd, NULL);
\r
6033 /* Destroy the menu.*/
\r
6034 DestroyMenu(hmenu);
\r
6039 int sizeX, sizeY, newSizeX, newSizeY;
\r
6041 } ResizeEditPlusButtonsClosure;
\r
6044 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
6046 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
6050 if (hChild == cl->hText) return TRUE;
\r
6051 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
6052 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
6053 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
6054 ScreenToClient(cl->hDlg, &pt);
\r
6055 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
6056 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
6060 /* Resize a dialog that has a (rich) edit field filling most of
\r
6061 the top, with a row of buttons below */
\r
6063 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
6066 int newTextHeight, newTextWidth;
\r
6067 ResizeEditPlusButtonsClosure cl;
\r
6069 /*if (IsIconic(hDlg)) return;*/
\r
6070 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
6072 cl.hdwp = BeginDeferWindowPos(8);
\r
6074 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
6075 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6076 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6077 if (newTextHeight < 0) {
\r
6078 newSizeY += -newTextHeight;
\r
6079 newTextHeight = 0;
\r
6081 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
6082 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6088 cl.newSizeX = newSizeX;
\r
6089 cl.newSizeY = newSizeY;
\r
6090 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
6092 EndDeferWindowPos(cl.hdwp);
\r
6095 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
6097 RECT rChild, rParent;
\r
6098 int wChild, hChild, wParent, hParent;
\r
6099 int wScreen, hScreen, xNew, yNew;
\r
6102 /* Get the Height and Width of the child window */
\r
6103 GetWindowRect (hwndChild, &rChild);
\r
6104 wChild = rChild.right - rChild.left;
\r
6105 hChild = rChild.bottom - rChild.top;
\r
6107 /* Get the Height and Width of the parent window */
\r
6108 GetWindowRect (hwndParent, &rParent);
\r
6109 wParent = rParent.right - rParent.left;
\r
6110 hParent = rParent.bottom - rParent.top;
\r
6112 /* Get the display limits */
\r
6113 hdc = GetDC (hwndChild);
\r
6114 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
6115 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
6116 ReleaseDC(hwndChild, hdc);
\r
6118 /* Calculate new X position, then adjust for screen */
\r
6119 xNew = rParent.left + ((wParent - wChild) /2);
\r
6122 } else if ((xNew+wChild) > wScreen) {
\r
6123 xNew = wScreen - wChild;
\r
6126 /* Calculate new Y position, then adjust for screen */
\r
6128 yNew = rParent.top + ((hParent - hChild) /2);
\r
6131 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
6136 } else if ((yNew+hChild) > hScreen) {
\r
6137 yNew = hScreen - hChild;
\r
6140 /* Set it, and return */
\r
6141 return SetWindowPos (hwndChild, NULL,
\r
6142 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
6145 /* Center one window over another */
\r
6146 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
6148 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
6151 /*---------------------------------------------------------------------------*\
\r
6153 * Startup Dialog functions
\r
6155 \*---------------------------------------------------------------------------*/
\r
6157 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
6159 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6161 while (*cd != NULL) {
\r
6162 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
6168 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
6170 char buf1[MAX_ARG_LEN];
\r
6173 if (str[0] == '@') {
\r
6174 FILE* f = fopen(str + 1, "r");
\r
6176 DisplayFatalError(str + 1, errno, 2);
\r
6179 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
6181 buf1[len] = NULLCHAR;
\r
6185 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6188 char buf[MSG_SIZ];
\r
6189 char *end = strchr(str, '\n');
\r
6190 if (end == NULL) return;
\r
6191 memcpy(buf, str, end - str);
\r
6192 buf[end - str] = NULLCHAR;
\r
6193 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6199 SetStartupDialogEnables(HWND hDlg)
\r
6201 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6202 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6203 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6204 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6205 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6206 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6207 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6208 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6209 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6210 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6211 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6212 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6213 IsDlgButtonChecked(hDlg, OPT_View));
\r
6217 QuoteForFilename(char *filename)
\r
6219 int dquote, space;
\r
6220 dquote = strchr(filename, '"') != NULL;
\r
6221 space = strchr(filename, ' ') != NULL;
\r
6222 if (dquote || space) {
\r
6234 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6236 char buf[MSG_SIZ];
\r
6239 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6240 q = QuoteForFilename(nthcp);
\r
6241 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6242 if (*nthdir != NULLCHAR) {
\r
6243 q = QuoteForFilename(nthdir);
\r
6244 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6246 if (*nthcp == NULLCHAR) {
\r
6247 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6248 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6249 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6250 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6255 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6257 char buf[MSG_SIZ];
\r
6261 switch (message) {
\r
6262 case WM_INITDIALOG:
\r
6263 /* Center the dialog */
\r
6264 CenterWindow (hDlg, GetDesktopWindow());
\r
6265 Translate(hDlg, DLG_Startup);
\r
6266 /* Initialize the dialog items */
\r
6267 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6268 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6269 firstChessProgramNames);
\r
6270 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6271 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6272 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6273 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6274 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6275 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6276 if (*appData.icsHelper != NULLCHAR) {
\r
6277 char *q = QuoteForFilename(appData.icsHelper);
\r
6278 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6280 if (*appData.icsHost == NULLCHAR) {
\r
6281 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6282 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6283 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6284 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6285 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6288 if (appData.icsActive) {
\r
6289 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6291 else if (appData.noChessProgram) {
\r
6292 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6295 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6298 SetStartupDialogEnables(hDlg);
\r
6302 switch (LOWORD(wParam)) {
\r
6304 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6305 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6306 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6308 comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox
\r
6309 ParseArgs(StringGet, &p);
\r
6310 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6311 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6313 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6314 ParseArgs(StringGet, &p);
\r
6315 SwapEngines(singleList); // ... and then make it 'second'
\r
6317 appData.noChessProgram = FALSE;
\r
6318 appData.icsActive = FALSE;
\r
6319 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6320 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6321 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6323 ParseArgs(StringGet, &p);
\r
6324 if (appData.zippyPlay) {
\r
6325 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6326 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6328 ParseArgs(StringGet, &p);
\r
6330 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6331 appData.noChessProgram = TRUE;
\r
6332 appData.icsActive = FALSE;
\r
6334 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6335 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6338 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6339 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6341 ParseArgs(StringGet, &p);
\r
6343 EndDialog(hDlg, TRUE);
\r
6350 case IDM_HELPCONTENTS:
\r
6351 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6352 MessageBox (GetFocus(),
\r
6353 _("Unable to activate help"),
\r
6354 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6359 SetStartupDialogEnables(hDlg);
\r
6367 /*---------------------------------------------------------------------------*\
\r
6369 * About box dialog functions
\r
6371 \*---------------------------------------------------------------------------*/
\r
6373 /* Process messages for "About" dialog box */
\r
6375 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6377 switch (message) {
\r
6378 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6379 /* Center the dialog over the application window */
\r
6380 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6381 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6382 Translate(hDlg, ABOUTBOX);
\r
6386 case WM_COMMAND: /* message: received a command */
\r
6387 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6388 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6389 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6397 /*---------------------------------------------------------------------------*\
\r
6399 * Comment Dialog functions
\r
6401 \*---------------------------------------------------------------------------*/
\r
6404 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6406 static HANDLE hwndText = NULL;
\r
6407 int len, newSizeX, newSizeY;
\r
6408 static int sizeX, sizeY;
\r
6413 switch (message) {
\r
6414 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6415 /* Initialize the dialog items */
\r
6416 Translate(hDlg, DLG_EditComment);
\r
6417 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6418 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6419 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6420 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6421 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6422 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6423 SetWindowText(hDlg, commentTitle);
\r
6424 if (editComment) {
\r
6425 SetFocus(hwndText);
\r
6427 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6429 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6430 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6431 MAKELPARAM(FALSE, 0));
\r
6432 /* Size and position the dialog */
\r
6433 if (!commentDialog) {
\r
6434 commentDialog = hDlg;
\r
6435 GetClientRect(hDlg, &rect);
\r
6436 sizeX = rect.right;
\r
6437 sizeY = rect.bottom;
\r
6438 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6439 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6440 WINDOWPLACEMENT wp;
\r
6441 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6442 wp.length = sizeof(WINDOWPLACEMENT);
\r
6444 wp.showCmd = SW_SHOW;
\r
6445 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6446 wp.rcNormalPosition.left = wpComment.x;
\r
6447 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6448 wp.rcNormalPosition.top = wpComment.y;
\r
6449 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6450 SetWindowPlacement(hDlg, &wp);
\r
6452 GetClientRect(hDlg, &rect);
\r
6453 newSizeX = rect.right;
\r
6454 newSizeY = rect.bottom;
\r
6455 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6456 newSizeX, newSizeY);
\r
6461 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6464 case WM_COMMAND: /* message: received a command */
\r
6465 switch (LOWORD(wParam)) {
\r
6467 if (editComment) {
\r
6469 /* Read changed options from the dialog box */
\r
6470 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6471 len = GetWindowTextLength(hwndText);
\r
6472 str = (char *) malloc(len + 1);
\r
6473 GetWindowText(hwndText, str, len + 1);
\r
6482 ReplaceComment(commentIndex, str);
\r
6489 case OPT_CancelComment:
\r
6493 case OPT_ClearComment:
\r
6494 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6497 case OPT_EditComment:
\r
6498 EditCommentEvent();
\r
6506 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6507 if( wParam == OPT_CommentText ) {
\r
6508 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6510 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6511 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6515 pt.x = LOWORD( lpMF->lParam );
\r
6516 pt.y = HIWORD( lpMF->lParam );
\r
6518 if(lpMF->msg == WM_CHAR) {
\r
6520 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6521 index = sel.cpMin;
\r
6523 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6525 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6526 len = GetWindowTextLength(hwndText);
\r
6527 str = (char *) malloc(len + 1);
\r
6528 GetWindowText(hwndText, str, len + 1);
\r
6529 ReplaceComment(commentIndex, str);
\r
6530 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6531 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6534 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6535 lpMF->msg = WM_USER;
\r
6543 newSizeX = LOWORD(lParam);
\r
6544 newSizeY = HIWORD(lParam);
\r
6545 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6550 case WM_GETMINMAXINFO:
\r
6551 /* Prevent resizing window too small */
\r
6552 mmi = (MINMAXINFO *) lParam;
\r
6553 mmi->ptMinTrackSize.x = 100;
\r
6554 mmi->ptMinTrackSize.y = 100;
\r
6561 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6566 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6568 if (str == NULL) str = "";
\r
6569 p = (char *) malloc(2 * strlen(str) + 2);
\r
6572 if (*str == '\n') *q++ = '\r';
\r
6576 if (commentText != NULL) free(commentText);
\r
6578 commentIndex = index;
\r
6579 commentTitle = title;
\r
6581 editComment = edit;
\r
6583 if (commentDialog) {
\r
6584 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6585 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6587 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6588 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6589 hwndMain, (DLGPROC)lpProc);
\r
6590 FreeProcInstance(lpProc);
\r
6596 /*---------------------------------------------------------------------------*\
\r
6598 * Type-in move dialog functions
\r
6600 \*---------------------------------------------------------------------------*/
\r
6603 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6605 char move[MSG_SIZ];
\r
6608 switch (message) {
\r
6609 case WM_INITDIALOG:
\r
6610 move[0] = (char) lParam;
\r
6611 move[1] = NULLCHAR;
\r
6612 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6613 Translate(hDlg, DLG_TypeInMove);
\r
6614 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6615 SetWindowText(hInput, move);
\r
6617 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6621 switch (LOWORD(wParam)) {
\r
6624 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6625 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6626 TypeInDoneEvent(move);
\r
6627 EndDialog(hDlg, TRUE);
\r
6630 EndDialog(hDlg, FALSE);
\r
6641 PopUpMoveDialog(char firstchar)
\r
6645 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6646 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6647 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6648 FreeProcInstance(lpProc);
\r
6651 /*---------------------------------------------------------------------------*\
\r
6653 * Type-in name dialog functions
\r
6655 \*---------------------------------------------------------------------------*/
\r
6658 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6660 char move[MSG_SIZ];
\r
6663 switch (message) {
\r
6664 case WM_INITDIALOG:
\r
6665 move[0] = (char) lParam;
\r
6666 move[1] = NULLCHAR;
\r
6667 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6668 Translate(hDlg, DLG_TypeInName);
\r
6669 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6670 SetWindowText(hInput, move);
\r
6672 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6676 switch (LOWORD(wParam)) {
\r
6678 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6679 appData.userName = strdup(move);
\r
6682 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6683 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6684 DisplayTitle(move);
\r
6688 EndDialog(hDlg, TRUE);
\r
6691 EndDialog(hDlg, FALSE);
\r
6702 PopUpNameDialog(char firstchar)
\r
6706 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6707 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6708 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6709 FreeProcInstance(lpProc);
\r
6712 /*---------------------------------------------------------------------------*\
\r
6716 \*---------------------------------------------------------------------------*/
\r
6718 /* Nonmodal error box */
\r
6719 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6720 WPARAM wParam, LPARAM lParam);
\r
6723 ErrorPopUp(char *title, char *content)
\r
6727 BOOLEAN modal = hwndMain == NULL;
\r
6745 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6746 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6749 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6751 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6752 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6753 hwndMain, (DLGPROC)lpProc);
\r
6754 FreeProcInstance(lpProc);
\r
6761 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6762 if (errorDialog == NULL) return;
\r
6763 DestroyWindow(errorDialog);
\r
6764 errorDialog = NULL;
\r
6765 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6769 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6773 switch (message) {
\r
6774 case WM_INITDIALOG:
\r
6775 GetWindowRect(hDlg, &rChild);
\r
6778 SetWindowPos(hDlg, NULL, rChild.left,
\r
6779 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6780 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6784 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6785 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6786 and it doesn't work when you resize the dialog.
\r
6787 For now, just give it a default position.
\r
6789 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6790 Translate(hDlg, DLG_Error);
\r
6792 errorDialog = hDlg;
\r
6793 SetWindowText(hDlg, errorTitle);
\r
6794 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6798 switch (LOWORD(wParam)) {
\r
6801 if (errorDialog == hDlg) errorDialog = NULL;
\r
6802 DestroyWindow(hDlg);
\r
6814 HWND gothicDialog = NULL;
\r
6817 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6820 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6822 switch (message) {
\r
6823 case WM_INITDIALOG:
\r
6824 GetWindowRect(hDlg, &rChild);
\r
6826 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6830 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6831 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6832 and it doesn't work when you resize the dialog.
\r
6833 For now, just give it a default position.
\r
6835 gothicDialog = hDlg;
\r
6836 SetWindowText(hDlg, errorTitle);
\r
6837 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6841 switch (LOWORD(wParam)) {
\r
6844 if (errorDialog == hDlg) errorDialog = NULL;
\r
6845 DestroyWindow(hDlg);
\r
6857 GothicPopUp(char *title, VariantClass variant)
\r
6860 static char *lastTitle;
\r
6862 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6863 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6865 if(lastTitle != title && gothicDialog != NULL) {
\r
6866 DestroyWindow(gothicDialog);
\r
6867 gothicDialog = NULL;
\r
6869 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6870 title = lastTitle;
\r
6871 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6872 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6873 hwndMain, (DLGPROC)lpProc);
\r
6874 FreeProcInstance(lpProc);
\r
6879 /*---------------------------------------------------------------------------*\
\r
6881 * Ics Interaction console functions
\r
6883 \*---------------------------------------------------------------------------*/
\r
6885 #define HISTORY_SIZE 64
\r
6886 static char *history[HISTORY_SIZE];
\r
6887 int histIn = 0, histP = 0;
\r
6891 SaveInHistory(char *cmd)
\r
6893 if (history[histIn] != NULL) {
\r
6894 free(history[histIn]);
\r
6895 history[histIn] = NULL;
\r
6897 if (*cmd == NULLCHAR) return;
\r
6898 history[histIn] = StrSave(cmd);
\r
6899 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6900 if (history[histIn] != NULL) {
\r
6901 free(history[histIn]);
\r
6903 history[histIn] = NULL;
\r
6909 PrevInHistory(char *cmd)
\r
6912 if (histP == histIn) {
\r
6913 if (history[histIn] != NULL) free(history[histIn]);
\r
6914 history[histIn] = StrSave(cmd);
\r
6916 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6917 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6919 return history[histP];
\r
6925 if (histP == histIn) return NULL;
\r
6926 histP = (histP + 1) % HISTORY_SIZE;
\r
6927 return history[histP];
\r
6931 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6935 hmenu = LoadMenu(hInst, "TextMenu");
\r
6936 h = GetSubMenu(hmenu, 0);
\r
6938 if (strcmp(e->item, "-") == 0) {
\r
6939 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6940 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6941 int flags = MF_STRING, j = 0;
\r
6942 if (e->item[0] == '|') {
\r
6943 flags |= MF_MENUBARBREAK;
\r
6946 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6947 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6955 WNDPROC consoleTextWindowProc;
\r
6958 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6960 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6961 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6965 SetWindowText(hInput, command);
\r
6967 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6969 sel.cpMin = 999999;
\r
6970 sel.cpMax = 999999;
\r
6971 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6976 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6977 if (sel.cpMin == sel.cpMax) {
\r
6978 /* Expand to surrounding word */
\r
6981 tr.chrg.cpMax = sel.cpMin;
\r
6982 tr.chrg.cpMin = --sel.cpMin;
\r
6983 if (sel.cpMin < 0) break;
\r
6984 tr.lpstrText = name;
\r
6985 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6986 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6990 tr.chrg.cpMin = sel.cpMax;
\r
6991 tr.chrg.cpMax = ++sel.cpMax;
\r
6992 tr.lpstrText = name;
\r
6993 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6994 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6997 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6998 MessageBeep(MB_ICONEXCLAMATION);
\r
7002 tr.lpstrText = name;
\r
7003 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
7005 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
7006 MessageBeep(MB_ICONEXCLAMATION);
\r
7009 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
7012 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
7013 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
7014 SetWindowText(hInput, buf);
\r
7015 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
7017 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
7018 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
7019 SetWindowText(hInput, buf);
\r
7020 sel.cpMin = 999999;
\r
7021 sel.cpMax = 999999;
\r
7022 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7028 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7033 switch (message) {
\r
7035 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7036 if(wParam=='R') return 0;
\r
7039 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
7042 sel.cpMin = 999999;
\r
7043 sel.cpMax = 999999;
\r
7044 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7045 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
7050 if(wParam != '\022') {
\r
7051 if (wParam == '\t') {
\r
7052 if (GetKeyState(VK_SHIFT) < 0) {
\r
7054 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7055 if (buttonDesc[0].hwnd) {
\r
7056 SetFocus(buttonDesc[0].hwnd);
\r
7058 SetFocus(hwndMain);
\r
7062 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
7065 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7066 JAWS_DELETE( SetFocus(hInput); )
\r
7067 SendMessage(hInput, message, wParam, lParam);
\r
7070 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
7072 case WM_RBUTTONDOWN:
\r
7073 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
7074 /* Move selection here if it was empty */
\r
7076 pt.x = LOWORD(lParam);
\r
7077 pt.y = HIWORD(lParam);
\r
7078 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7079 if (sel.cpMin == sel.cpMax) {
\r
7080 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
7081 sel.cpMax = sel.cpMin;
\r
7082 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7084 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
7085 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
7087 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
7088 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7089 if (sel.cpMin == sel.cpMax) {
\r
7090 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7091 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
7093 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7094 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7096 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
7097 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
7098 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
7099 MenuPopup(hwnd, pt, hmenu, -1);
\r
7103 case WM_RBUTTONUP:
\r
7104 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7105 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7106 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7110 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7112 return SendMessage(hInput, message, wParam, lParam);
\r
7113 case WM_MBUTTONDOWN:
\r
7114 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7116 switch (LOWORD(wParam)) {
\r
7117 case IDM_QuickPaste:
\r
7119 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7120 if (sel.cpMin == sel.cpMax) {
\r
7121 MessageBeep(MB_ICONEXCLAMATION);
\r
7124 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7125 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7126 SendMessage(hInput, WM_PASTE, 0, 0);
\r
7131 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7134 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7137 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7141 int i = LOWORD(wParam) - IDM_CommandX;
\r
7142 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
7143 icsTextMenuEntry[i].command != NULL) {
\r
7144 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
7145 icsTextMenuEntry[i].getname,
\r
7146 icsTextMenuEntry[i].immediate);
\r
7154 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
7157 WNDPROC consoleInputWindowProc;
\r
7160 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7162 char buf[MSG_SIZ];
\r
7164 static BOOL sendNextChar = FALSE;
\r
7165 static BOOL quoteNextChar = FALSE;
\r
7166 InputSource *is = consoleInputSource;
\r
7170 switch (message) {
\r
7172 if (!appData.localLineEditing || sendNextChar) {
\r
7173 is->buf[0] = (CHAR) wParam;
\r
7175 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7176 sendNextChar = FALSE;
\r
7179 if (quoteNextChar) {
\r
7180 buf[0] = (char) wParam;
\r
7181 buf[1] = NULLCHAR;
\r
7182 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7183 quoteNextChar = FALSE;
\r
7187 case '\r': /* Enter key */
\r
7188 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7189 if (consoleEcho) SaveInHistory(is->buf);
\r
7190 is->buf[is->count++] = '\n';
\r
7191 is->buf[is->count] = NULLCHAR;
\r
7192 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7193 if (consoleEcho) {
\r
7194 ConsoleOutput(is->buf, is->count, TRUE);
\r
7195 } else if (appData.localLineEditing) {
\r
7196 ConsoleOutput("\n", 1, TRUE);
\r
7199 case '\033': /* Escape key */
\r
7200 SetWindowText(hwnd, "");
\r
7201 cf.cbSize = sizeof(CHARFORMAT);
\r
7202 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7203 if (consoleEcho) {
\r
7204 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7206 cf.crTextColor = COLOR_ECHOOFF;
\r
7208 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7209 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7211 case '\t': /* Tab key */
\r
7212 if (GetKeyState(VK_SHIFT) < 0) {
\r
7214 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7217 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7218 if (buttonDesc[0].hwnd) {
\r
7219 SetFocus(buttonDesc[0].hwnd);
\r
7221 SetFocus(hwndMain);
\r
7225 case '\023': /* Ctrl+S */
\r
7226 sendNextChar = TRUE;
\r
7228 case '\021': /* Ctrl+Q */
\r
7229 quoteNextChar = TRUE;
\r
7239 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7240 p = PrevInHistory(buf);
\r
7242 SetWindowText(hwnd, p);
\r
7243 sel.cpMin = 999999;
\r
7244 sel.cpMax = 999999;
\r
7245 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7250 p = NextInHistory();
\r
7252 SetWindowText(hwnd, p);
\r
7253 sel.cpMin = 999999;
\r
7254 sel.cpMax = 999999;
\r
7255 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7261 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7265 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7269 case WM_MBUTTONDOWN:
\r
7270 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7271 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7273 case WM_RBUTTONUP:
\r
7274 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7275 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7276 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7280 hmenu = LoadMenu(hInst, "InputMenu");
\r
7281 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7282 if (sel.cpMin == sel.cpMax) {
\r
7283 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7284 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7286 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7287 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7289 pt.x = LOWORD(lParam);
\r
7290 pt.y = HIWORD(lParam);
\r
7291 MenuPopup(hwnd, pt, hmenu, -1);
\r
7295 switch (LOWORD(wParam)) {
\r
7297 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7299 case IDM_SelectAll:
\r
7301 sel.cpMax = -1; /*999999?*/
\r
7302 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7305 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7308 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7311 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7316 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7319 #define CO_MAX 100000
\r
7320 #define CO_TRIM 1000
\r
7323 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7325 static SnapData sd;
\r
7326 HWND hText, hInput;
\r
7328 static int sizeX, sizeY;
\r
7329 int newSizeX, newSizeY;
\r
7333 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7334 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7336 switch (message) {
\r
7338 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7340 ENLINK *pLink = (ENLINK*)lParam;
\r
7341 if (pLink->msg == WM_LBUTTONUP)
\r
7345 tr.chrg = pLink->chrg;
\r
7346 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7347 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7348 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7349 free(tr.lpstrText);
\r
7353 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7354 hwndConsole = hDlg;
\r
7356 consoleTextWindowProc = (WNDPROC)
\r
7357 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7358 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7359 consoleInputWindowProc = (WNDPROC)
\r
7360 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7361 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7362 Colorize(ColorNormal, TRUE);
\r
7363 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7364 ChangedConsoleFont();
\r
7365 GetClientRect(hDlg, &rect);
\r
7366 sizeX = rect.right;
\r
7367 sizeY = rect.bottom;
\r
7368 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7369 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7370 WINDOWPLACEMENT wp;
\r
7371 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7372 wp.length = sizeof(WINDOWPLACEMENT);
\r
7374 wp.showCmd = SW_SHOW;
\r
7375 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7376 wp.rcNormalPosition.left = wpConsole.x;
\r
7377 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7378 wp.rcNormalPosition.top = wpConsole.y;
\r
7379 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7380 SetWindowPlacement(hDlg, &wp);
\r
7383 // [HGM] Chessknight's change 2004-07-13
\r
7384 else { /* Determine Defaults */
\r
7385 WINDOWPLACEMENT wp;
\r
7386 wpConsole.x = wpMain.width + 1;
\r
7387 wpConsole.y = wpMain.y;
\r
7388 wpConsole.width = screenWidth - wpMain.width;
\r
7389 wpConsole.height = wpMain.height;
\r
7390 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7391 wp.length = sizeof(WINDOWPLACEMENT);
\r
7393 wp.showCmd = SW_SHOW;
\r
7394 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7395 wp.rcNormalPosition.left = wpConsole.x;
\r
7396 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7397 wp.rcNormalPosition.top = wpConsole.y;
\r
7398 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7399 SetWindowPlacement(hDlg, &wp);
\r
7402 // Allow hText to highlight URLs and send notifications on them
\r
7403 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7404 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7405 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7406 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7420 if (IsIconic(hDlg)) break;
\r
7421 newSizeX = LOWORD(lParam);
\r
7422 newSizeY = HIWORD(lParam);
\r
7423 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7424 RECT rectText, rectInput;
\r
7426 int newTextHeight, newTextWidth;
\r
7427 GetWindowRect(hText, &rectText);
\r
7428 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7429 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7430 if (newTextHeight < 0) {
\r
7431 newSizeY += -newTextHeight;
\r
7432 newTextHeight = 0;
\r
7434 SetWindowPos(hText, NULL, 0, 0,
\r
7435 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7436 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7437 pt.x = rectInput.left;
\r
7438 pt.y = rectInput.top + newSizeY - sizeY;
\r
7439 ScreenToClient(hDlg, &pt);
\r
7440 SetWindowPos(hInput, NULL,
\r
7441 pt.x, pt.y, /* needs client coords */
\r
7442 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7443 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7449 case WM_GETMINMAXINFO:
\r
7450 /* Prevent resizing window too small */
\r
7451 mmi = (MINMAXINFO *) lParam;
\r
7452 mmi->ptMinTrackSize.x = 100;
\r
7453 mmi->ptMinTrackSize.y = 100;
\r
7456 /* [AS] Snapping */
\r
7457 case WM_ENTERSIZEMOVE:
\r
7458 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7461 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7464 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7466 case WM_EXITSIZEMOVE:
\r
7467 UpdateICSWidth(hText);
\r
7468 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7471 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7479 if (hwndConsole) return;
\r
7480 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7481 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7486 ConsoleOutput(char* data, int length, int forceVisible)
\r
7491 char buf[CO_MAX+1];
\r
7494 static int delayLF = 0;
\r
7495 CHARRANGE savesel, sel;
\r
7497 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7505 while (length--) {
\r
7513 } else if (*p == '\007') {
\r
7514 MyPlaySound(&sounds[(int)SoundBell]);
\r
7521 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7522 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7523 /* Save current selection */
\r
7524 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7525 exlen = GetWindowTextLength(hText);
\r
7526 /* Find out whether current end of text is visible */
\r
7527 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7528 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7529 /* Trim existing text if it's too long */
\r
7530 if (exlen + (q - buf) > CO_MAX) {
\r
7531 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7534 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7535 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7537 savesel.cpMin -= trim;
\r
7538 savesel.cpMax -= trim;
\r
7539 if (exlen < 0) exlen = 0;
\r
7540 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7541 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7543 /* Append the new text */
\r
7544 sel.cpMin = exlen;
\r
7545 sel.cpMax = exlen;
\r
7546 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7547 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7548 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7549 if (forceVisible || exlen == 0 ||
\r
7550 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7551 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7552 /* Scroll to make new end of text visible if old end of text
\r
7553 was visible or new text is an echo of user typein */
\r
7554 sel.cpMin = 9999999;
\r
7555 sel.cpMax = 9999999;
\r
7556 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7557 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7558 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7559 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7561 if (savesel.cpMax == exlen || forceVisible) {
\r
7562 /* Move insert point to new end of text if it was at the old
\r
7563 end of text or if the new text is an echo of user typein */
\r
7564 sel.cpMin = 9999999;
\r
7565 sel.cpMax = 9999999;
\r
7566 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7568 /* Restore previous selection */
\r
7569 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7571 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7578 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7582 COLORREF oldFg, oldBg;
\r
7586 if(copyNumber > 1)
\r
7587 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7589 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7590 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7591 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7594 rect.right = x + squareSize;
\r
7596 rect.bottom = y + squareSize;
\r
7599 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7600 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7601 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7602 &rect, str, strlen(str), NULL);
\r
7604 (void) SetTextColor(hdc, oldFg);
\r
7605 (void) SetBkColor(hdc, oldBg);
\r
7606 (void) SelectObject(hdc, oldFont);
\r
7610 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7611 RECT *rect, char *color, char *flagFell)
\r
7615 COLORREF oldFg, oldBg;
\r
7618 if (twoBoards && partnerUp) return;
\r
7619 if (appData.clockMode) {
\r
7621 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7623 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7630 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7631 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7633 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7634 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7636 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7640 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7641 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7642 rect, str, strlen(str), NULL);
\r
7643 if(logoHeight > 0 && appData.clockMode) {
\r
7645 str += strlen(color)+2;
\r
7646 r.top = rect->top + logoHeight/2;
\r
7647 r.left = rect->left;
\r
7648 r.right = rect->right;
\r
7649 r.bottom = rect->bottom;
\r
7650 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7651 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7652 &r, str, strlen(str), NULL);
\r
7654 (void) SetTextColor(hdc, oldFg);
\r
7655 (void) SetBkColor(hdc, oldBg);
\r
7656 (void) SelectObject(hdc, oldFont);
\r
7661 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7667 if( count <= 0 ) {
\r
7668 if (appData.debugMode) {
\r
7669 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7672 return ERROR_INVALID_USER_BUFFER;
\r
7675 ResetEvent(ovl->hEvent);
\r
7676 ovl->Offset = ovl->OffsetHigh = 0;
\r
7677 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7681 err = GetLastError();
\r
7682 if (err == ERROR_IO_PENDING) {
\r
7683 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7687 err = GetLastError();
\r
7694 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7699 ResetEvent(ovl->hEvent);
\r
7700 ovl->Offset = ovl->OffsetHigh = 0;
\r
7701 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7705 err = GetLastError();
\r
7706 if (err == ERROR_IO_PENDING) {
\r
7707 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7711 err = GetLastError();
\r
7718 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7719 void CheckForInputBufferFull( InputSource * is )
\r
7721 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7722 /* Look for end of line */
\r
7723 char * p = is->buf;
\r
7725 while( p < is->next && *p != '\n' ) {
\r
7729 if( p >= is->next ) {
\r
7730 if (appData.debugMode) {
\r
7731 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7734 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7735 is->count = (DWORD) -1;
\r
7736 is->next = is->buf;
\r
7742 InputThread(LPVOID arg)
\r
7747 is = (InputSource *) arg;
\r
7748 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7749 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7750 while (is->hThread != NULL) {
\r
7751 is->error = DoReadFile(is->hFile, is->next,
\r
7752 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7753 &is->count, &ovl);
\r
7754 if (is->error == NO_ERROR) {
\r
7755 is->next += is->count;
\r
7757 if (is->error == ERROR_BROKEN_PIPE) {
\r
7758 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7761 is->count = (DWORD) -1;
\r
7762 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7767 CheckForInputBufferFull( is );
\r
7769 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7771 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7773 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7776 CloseHandle(ovl.hEvent);
\r
7777 CloseHandle(is->hFile);
\r
7779 if (appData.debugMode) {
\r
7780 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7787 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7789 NonOvlInputThread(LPVOID arg)
\r
7796 is = (InputSource *) arg;
\r
7797 while (is->hThread != NULL) {
\r
7798 is->error = ReadFile(is->hFile, is->next,
\r
7799 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7800 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7801 if (is->error == NO_ERROR) {
\r
7802 /* Change CRLF to LF */
\r
7803 if (is->next > is->buf) {
\r
7805 i = is->count + 1;
\r
7813 if (prev == '\r' && *p == '\n') {
\r
7825 if (is->error == ERROR_BROKEN_PIPE) {
\r
7826 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7829 is->count = (DWORD) -1;
\r
7833 CheckForInputBufferFull( is );
\r
7835 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7837 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7839 if (is->count < 0) break; /* Quit on error */
\r
7841 CloseHandle(is->hFile);
\r
7846 SocketInputThread(LPVOID arg)
\r
7850 is = (InputSource *) arg;
\r
7851 while (is->hThread != NULL) {
\r
7852 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7853 if ((int)is->count == SOCKET_ERROR) {
\r
7854 is->count = (DWORD) -1;
\r
7855 is->error = WSAGetLastError();
\r
7857 is->error = NO_ERROR;
\r
7858 is->next += is->count;
\r
7859 if (is->count == 0 && is->second == is) {
\r
7860 /* End of file on stderr; quit with no message */
\r
7864 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7866 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7868 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7874 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7878 is = (InputSource *) lParam;
\r
7879 if (is->lineByLine) {
\r
7880 /* Feed in lines one by one */
\r
7881 char *p = is->buf;
\r
7883 while (q < is->next) {
\r
7884 if (*q++ == '\n') {
\r
7885 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7890 /* Move any partial line to the start of the buffer */
\r
7892 while (p < is->next) {
\r
7897 if (is->error != NO_ERROR || is->count == 0) {
\r
7898 /* Notify backend of the error. Note: If there was a partial
\r
7899 line at the end, it is not flushed through. */
\r
7900 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7903 /* Feed in the whole chunk of input at once */
\r
7904 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7905 is->next = is->buf;
\r
7909 /*---------------------------------------------------------------------------*\
\r
7911 * Menu enables. Used when setting various modes.
\r
7913 \*---------------------------------------------------------------------------*/
\r
7921 GreyRevert(Boolean grey)
\r
7922 { // [HGM] vari: for retracting variations in local mode
\r
7923 HMENU hmenu = GetMenu(hwndMain);
\r
7924 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7925 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7929 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7931 while (enab->item > 0) {
\r
7932 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7937 Enables gnuEnables[] = {
\r
7938 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7939 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7940 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7941 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7942 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7943 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7944 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7945 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7946 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7947 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7948 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7949 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7950 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7952 // Needed to switch from ncp to GNU mode on Engine Load
\r
7953 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7954 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7955 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7956 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7957 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7958 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7959 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
7960 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7961 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
7962 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
7963 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7964 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7965 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7966 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7970 Enables icsEnables[] = {
\r
7971 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7972 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7973 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7974 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7975 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7976 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7977 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7978 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7979 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7980 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7981 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7982 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7983 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7984 { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },
\r
7985 { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },
\r
7986 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7987 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7988 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7989 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7990 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
7995 Enables zippyEnables[] = {
\r
7996 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7997 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7998 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7999 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
8004 Enables ncpEnables[] = {
\r
8005 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
8006 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
8007 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8008 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8009 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8010 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8011 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
8012 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
8013 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
8014 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
8015 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
8016 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
8017 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8018 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
8019 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
8020 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
8021 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
8022 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
8023 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
8024 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
8025 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
8026 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
8030 Enables trainingOnEnables[] = {
\r
8031 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
8032 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
8033 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
8034 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
8035 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
8036 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
8037 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
8038 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8039 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
8043 Enables trainingOffEnables[] = {
\r
8044 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
8045 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
8046 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
8047 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
8048 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
8049 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
8050 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
8051 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8052 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
8056 /* These modify either ncpEnables or gnuEnables */
\r
8057 Enables cmailEnables[] = {
\r
8058 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
8059 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
8060 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
8061 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
8062 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
8063 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
8064 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
8068 Enables machineThinkingEnables[] = {
\r
8069 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8070 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
8071 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
8072 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8073 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
8074 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8075 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8076 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8077 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8078 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
8079 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8080 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8081 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8082 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8083 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
8084 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
8088 Enables userThinkingEnables[] = {
\r
8089 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8090 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
8091 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
8092 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8093 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
8094 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8095 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8096 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8097 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8098 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
8099 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
8100 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
8101 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
8102 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
8103 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
8104 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
8108 /*---------------------------------------------------------------------------*\
\r
8110 * Front-end interface functions exported by XBoard.
\r
8111 * Functions appear in same order as prototypes in frontend.h.
\r
8113 \*---------------------------------------------------------------------------*/
\r
8115 CheckMark(UINT item, int state)
\r
8117 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
8123 static UINT prevChecked = 0;
\r
8124 static int prevPausing = 0;
\r
8127 if (pausing != prevPausing) {
\r
8128 prevPausing = pausing;
\r
8129 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
8130 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
8131 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
8134 switch (gameMode) {
\r
8135 case BeginningOfGame:
\r
8136 if (appData.icsActive)
\r
8137 nowChecked = IDM_IcsClient;
\r
8138 else if (appData.noChessProgram)
\r
8139 nowChecked = IDM_EditGame;
\r
8141 nowChecked = IDM_MachineBlack;
\r
8143 case MachinePlaysBlack:
\r
8144 nowChecked = IDM_MachineBlack;
\r
8146 case MachinePlaysWhite:
\r
8147 nowChecked = IDM_MachineWhite;
\r
8149 case TwoMachinesPlay:
\r
8150 nowChecked = IDM_TwoMachines;
\r
8153 nowChecked = IDM_AnalysisMode;
\r
8156 nowChecked = IDM_AnalyzeFile;
\r
8159 nowChecked = IDM_EditGame;
\r
8161 case PlayFromGameFile:
\r
8162 nowChecked = IDM_LoadGame;
\r
8164 case EditPosition:
\r
8165 nowChecked = IDM_EditPosition;
\r
8168 nowChecked = IDM_Training;
\r
8170 case IcsPlayingWhite:
\r
8171 case IcsPlayingBlack:
\r
8172 case IcsObserving:
\r
8174 nowChecked = IDM_IcsClient;
\r
8181 CheckMark(prevChecked, MF_UNCHECKED);
\r
8182 CheckMark(nowChecked, MF_CHECKED);
\r
8183 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8185 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8186 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8187 MF_BYCOMMAND|MF_ENABLED);
\r
8189 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8190 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8193 prevChecked = nowChecked;
\r
8195 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8196 if (appData.icsActive) {
\r
8197 if (appData.icsEngineAnalyze) {
\r
8198 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8200 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8203 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8209 HMENU hmenu = GetMenu(hwndMain);
\r
8210 SetMenuEnables(hmenu, icsEnables);
\r
8211 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8212 MF_BYCOMMAND|MF_ENABLED);
\r
8214 if (appData.zippyPlay) {
\r
8215 SetMenuEnables(hmenu, zippyEnables);
\r
8216 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8217 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8218 MF_BYCOMMAND|MF_ENABLED);
\r
8226 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8232 HMENU hmenu = GetMenu(hwndMain);
\r
8233 SetMenuEnables(hmenu, ncpEnables);
\r
8234 DrawMenuBar(hwndMain);
\r
8240 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8244 SetTrainingModeOn()
\r
8247 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8248 for (i = 0; i < N_BUTTONS; i++) {
\r
8249 if (buttonDesc[i].hwnd != NULL)
\r
8250 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8255 VOID SetTrainingModeOff()
\r
8258 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8259 for (i = 0; i < N_BUTTONS; i++) {
\r
8260 if (buttonDesc[i].hwnd != NULL)
\r
8261 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8267 SetUserThinkingEnables()
\r
8269 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8273 SetMachineThinkingEnables()
\r
8275 HMENU hMenu = GetMenu(hwndMain);
\r
8276 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8278 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8280 if (gameMode == MachinePlaysBlack) {
\r
8281 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8282 } else if (gameMode == MachinePlaysWhite) {
\r
8283 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8284 } else if (gameMode == TwoMachinesPlay) {
\r
8285 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8291 DisplayTitle(char *str)
\r
8293 char title[MSG_SIZ], *host;
\r
8294 if (str[0] != NULLCHAR) {
\r
8295 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8296 } else if (appData.icsActive) {
\r
8297 if (appData.icsCommPort[0] != NULLCHAR)
\r
8300 host = appData.icsHost;
\r
8301 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8302 } else if (appData.noChessProgram) {
\r
8303 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8305 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8306 strcat(title, ": ");
\r
8307 strcat(title, first.tidy);
\r
8309 SetWindowText(hwndMain, title);
\r
8314 DisplayMessage(char *str1, char *str2)
\r
8318 int remain = MESSAGE_TEXT_MAX - 1;
\r
8321 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8322 messageText[0] = NULLCHAR;
\r
8324 len = strlen(str1);
\r
8325 if (len > remain) len = remain;
\r
8326 strncpy(messageText, str1, len);
\r
8327 messageText[len] = NULLCHAR;
\r
8330 if (*str2 && remain >= 2) {
\r
8332 strcat(messageText, " ");
\r
8335 len = strlen(str2);
\r
8336 if (len > remain) len = remain;
\r
8337 strncat(messageText, str2, len);
\r
8339 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8340 safeStrCpy(lastMsg, messageText, MSG_SIZ);
\r
8342 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8346 hdc = GetDC(hwndMain);
\r
8347 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8348 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8349 &messageRect, messageText, strlen(messageText), NULL);
\r
8350 (void) SelectObject(hdc, oldFont);
\r
8351 (void) ReleaseDC(hwndMain, hdc);
\r
8355 DisplayError(char *str, int error)
\r
8357 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8361 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8363 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8364 NULL, error, LANG_NEUTRAL,
\r
8365 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8367 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8369 ErrorMap *em = errmap;
\r
8370 while (em->err != 0 && em->err != error) em++;
\r
8371 if (em->err != 0) {
\r
8372 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8374 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8379 ErrorPopUp(_("Error"), buf);
\r
8384 DisplayMoveError(char *str)
\r
8386 fromX = fromY = -1;
\r
8387 ClearHighlights();
\r
8388 DrawPosition(FALSE, NULL);
\r
8389 if (appData.popupMoveErrors) {
\r
8390 ErrorPopUp(_("Error"), str);
\r
8392 DisplayMessage(str, "");
\r
8393 moveErrorMessageUp = TRUE;
\r
8398 DisplayFatalError(char *str, int error, int exitStatus)
\r
8400 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8402 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8405 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8406 NULL, error, LANG_NEUTRAL,
\r
8407 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8409 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8411 ErrorMap *em = errmap;
\r
8412 while (em->err != 0 && em->err != error) em++;
\r
8413 if (em->err != 0) {
\r
8414 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8416 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8421 if (appData.debugMode) {
\r
8422 fprintf(debugFP, "%s: %s\n", label, str);
\r
8424 if (appData.popupExitMessage) {
\r
8425 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8426 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8428 ExitEvent(exitStatus);
\r
8433 DisplayInformation(char *str)
\r
8435 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8440 DisplayNote(char *str)
\r
8442 ErrorPopUp(_("Note"), str);
\r
8447 char *title, *question, *replyPrefix;
\r
8452 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8454 static QuestionParams *qp;
\r
8455 char reply[MSG_SIZ];
\r
8458 switch (message) {
\r
8459 case WM_INITDIALOG:
\r
8460 qp = (QuestionParams *) lParam;
\r
8461 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8462 Translate(hDlg, DLG_Question);
\r
8463 SetWindowText(hDlg, qp->title);
\r
8464 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8465 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8469 switch (LOWORD(wParam)) {
\r
8471 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8472 if (*reply) strcat(reply, " ");
\r
8473 len = strlen(reply);
\r
8474 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8475 strcat(reply, "\n");
\r
8476 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8477 EndDialog(hDlg, TRUE);
\r
8478 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8481 EndDialog(hDlg, FALSE);
\r
8492 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8494 QuestionParams qp;
\r
8498 qp.question = question;
\r
8499 qp.replyPrefix = replyPrefix;
\r
8501 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8502 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8503 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8504 FreeProcInstance(lpProc);
\r
8507 /* [AS] Pick FRC position */
\r
8508 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8510 static int * lpIndexFRC;
\r
8516 case WM_INITDIALOG:
\r
8517 lpIndexFRC = (int *) lParam;
\r
8519 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8520 Translate(hDlg, DLG_NewGameFRC);
\r
8522 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8523 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8524 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8525 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8530 switch( LOWORD(wParam) ) {
\r
8532 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8533 EndDialog( hDlg, 0 );
\r
8534 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8537 EndDialog( hDlg, 1 );
\r
8539 case IDC_NFG_Edit:
\r
8540 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8541 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8543 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8546 case IDC_NFG_Random:
\r
8547 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8548 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8561 int index = appData.defaultFrcPosition;
\r
8562 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8564 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8566 if( result == 0 ) {
\r
8567 appData.defaultFrcPosition = index;
\r
8573 /* [AS] Game list options. Refactored by HGM */
\r
8575 HWND gameListOptionsDialog;
\r
8577 // low-level front-end: clear text edit / list widget
\r
8582 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8585 // low-level front-end: clear text edit / list widget
\r
8587 GLT_DeSelectList()
\r
8589 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8592 // low-level front-end: append line to text edit / list widget
\r
8594 GLT_AddToList( char *name )
\r
8597 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8601 // low-level front-end: get line from text edit / list widget
\r
8603 GLT_GetFromList( int index, char *name )
\r
8606 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8612 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8614 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8615 int idx2 = idx1 + delta;
\r
8616 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8618 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8621 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8622 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8623 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8624 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8628 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8632 case WM_INITDIALOG:
\r
8633 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8635 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8636 Translate(hDlg, DLG_GameListOptions);
\r
8638 /* Initialize list */
\r
8639 GLT_TagsToList( lpUserGLT );
\r
8641 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8646 switch( LOWORD(wParam) ) {
\r
8649 EndDialog( hDlg, 0 );
\r
8652 EndDialog( hDlg, 1 );
\r
8655 case IDC_GLT_Default:
\r
8656 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8659 case IDC_GLT_Restore:
\r
8660 GLT_TagsToList( appData.gameListTags );
\r
8664 GLT_MoveSelection( hDlg, -1 );
\r
8667 case IDC_GLT_Down:
\r
8668 GLT_MoveSelection( hDlg, +1 );
\r
8678 int GameListOptions()
\r
8681 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8683 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8685 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8687 if( result == 0 ) {
\r
8688 /* [AS] Memory leak here! */
\r
8689 appData.gameListTags = strdup( lpUserGLT );
\r
8696 DisplayIcsInteractionTitle(char *str)
\r
8698 char consoleTitle[MSG_SIZ];
\r
8700 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8701 SetWindowText(hwndConsole, consoleTitle);
\r
8703 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
8704 char buf[MSG_SIZ], *p = buf, *q;
\r
8705 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
8707 q = strchr(p, ';');
\r
8709 if(*p) ChatPopUp(p);
\r
8713 SetActiveWindow(hwndMain);
\r
8717 DrawPosition(int fullRedraw, Board board)
\r
8719 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8722 void NotifyFrontendLogin()
\r
8725 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8731 fromX = fromY = -1;
\r
8732 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8733 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8734 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8735 dragInfo.lastpos = dragInfo.pos;
\r
8736 dragInfo.start.x = dragInfo.start.y = -1;
\r
8737 dragInfo.from = dragInfo.start;
\r
8739 DrawPosition(TRUE, NULL);
\r
8746 CommentPopUp(char *title, char *str)
\r
8748 HWND hwnd = GetActiveWindow();
\r
8749 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8751 SetActiveWindow(hwnd);
\r
8755 CommentPopDown(void)
\r
8757 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8758 if (commentDialog) {
\r
8759 ShowWindow(commentDialog, SW_HIDE);
\r
8761 commentUp = FALSE;
\r
8765 EditCommentPopUp(int index, char *title, char *str)
\r
8767 EitherCommentPopUp(index, title, str, TRUE);
\r
8774 MyPlaySound(&sounds[(int)SoundRoar]);
\r
8781 MyPlaySound(&sounds[(int)SoundMove]);
\r
8784 VOID PlayIcsWinSound()
\r
8786 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8789 VOID PlayIcsLossSound()
\r
8791 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8794 VOID PlayIcsDrawSound()
\r
8796 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8799 VOID PlayIcsUnfinishedSound()
\r
8801 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8807 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8813 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8821 consoleEcho = TRUE;
\r
8822 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8823 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8824 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8833 consoleEcho = FALSE;
\r
8834 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8835 /* This works OK: set text and background both to the same color */
\r
8837 cf.crTextColor = COLOR_ECHOOFF;
\r
8838 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8839 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8842 /* No Raw()...? */
\r
8844 void Colorize(ColorClass cc, int continuation)
\r
8846 currentColorClass = cc;
\r
8847 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8848 consoleCF.crTextColor = textAttribs[cc].color;
\r
8849 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8850 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8856 static char buf[MSG_SIZ];
\r
8857 DWORD bufsiz = MSG_SIZ;
\r
8859 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8860 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8862 if (!GetUserName(buf, &bufsiz)) {
\r
8863 /*DisplayError("Error getting user name", GetLastError());*/
\r
8864 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8872 static char buf[MSG_SIZ];
\r
8873 DWORD bufsiz = MSG_SIZ;
\r
8875 if (!GetComputerName(buf, &bufsiz)) {
\r
8876 /*DisplayError("Error getting host name", GetLastError());*/
\r
8877 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8884 ClockTimerRunning()
\r
8886 return clockTimerEvent != 0;
\r
8892 if (clockTimerEvent == 0) return FALSE;
\r
8893 KillTimer(hwndMain, clockTimerEvent);
\r
8894 clockTimerEvent = 0;
\r
8899 StartClockTimer(long millisec)
\r
8901 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8902 (UINT) millisec, NULL);
\r
8906 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8909 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8911 if(appData.noGUI) return;
\r
8912 hdc = GetDC(hwndMain);
\r
8913 if (!IsIconic(hwndMain)) {
\r
8914 DisplayAClock(hdc, timeRemaining, highlight,
\r
8915 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8917 if (highlight && iconCurrent == iconBlack) {
\r
8918 iconCurrent = iconWhite;
\r
8919 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8920 if (IsIconic(hwndMain)) {
\r
8921 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8924 (void) ReleaseDC(hwndMain, hdc);
\r
8926 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8930 DisplayBlackClock(long timeRemaining, int highlight)
\r
8933 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8936 if(appData.noGUI) return;
\r
8937 hdc = GetDC(hwndMain);
\r
8938 if (!IsIconic(hwndMain)) {
\r
8939 DisplayAClock(hdc, timeRemaining, highlight,
\r
8940 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8942 if (highlight && iconCurrent == iconWhite) {
\r
8943 iconCurrent = iconBlack;
\r
8944 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8945 if (IsIconic(hwndMain)) {
\r
8946 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8949 (void) ReleaseDC(hwndMain, hdc);
\r
8951 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8956 LoadGameTimerRunning()
\r
8958 return loadGameTimerEvent != 0;
\r
8962 StopLoadGameTimer()
\r
8964 if (loadGameTimerEvent == 0) return FALSE;
\r
8965 KillTimer(hwndMain, loadGameTimerEvent);
\r
8966 loadGameTimerEvent = 0;
\r
8971 StartLoadGameTimer(long millisec)
\r
8973 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8974 (UINT) millisec, NULL);
\r
8982 char fileTitle[MSG_SIZ];
\r
8984 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8985 f = OpenFileDialog(hwndMain, "a", defName,
\r
8986 appData.oldSaveStyle ? "gam" : "pgn",
\r
8988 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8990 SaveGame(f, 0, "");
\r
8997 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8999 if (delayedTimerEvent != 0) {
\r
9000 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
9001 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
9003 KillTimer(hwndMain, delayedTimerEvent);
\r
9004 delayedTimerEvent = 0;
\r
9005 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
9006 delayedTimerCallback();
\r
9008 delayedTimerCallback = cb;
\r
9009 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
9010 (UINT) millisec, NULL);
\r
9013 DelayedEventCallback
\r
9016 if (delayedTimerEvent) {
\r
9017 return delayedTimerCallback;
\r
9024 CancelDelayedEvent()
\r
9026 if (delayedTimerEvent) {
\r
9027 KillTimer(hwndMain, delayedTimerEvent);
\r
9028 delayedTimerEvent = 0;
\r
9032 DWORD GetWin32Priority(int nice)
\r
9033 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
9035 REALTIME_PRIORITY_CLASS 0x00000100
\r
9036 HIGH_PRIORITY_CLASS 0x00000080
\r
9037 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
9038 NORMAL_PRIORITY_CLASS 0x00000020
\r
9039 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
9040 IDLE_PRIORITY_CLASS 0x00000040
\r
9042 if (nice < -15) return 0x00000080;
\r
9043 if (nice < 0) return 0x00008000;
\r
9044 if (nice == 0) return 0x00000020;
\r
9045 if (nice < 15) return 0x00004000;
\r
9046 return 0x00000040;
\r
9049 void RunCommand(char *cmdLine)
\r
9051 /* Now create the child process. */
\r
9052 STARTUPINFO siStartInfo;
\r
9053 PROCESS_INFORMATION piProcInfo;
\r
9055 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9056 siStartInfo.lpReserved = NULL;
\r
9057 siStartInfo.lpDesktop = NULL;
\r
9058 siStartInfo.lpTitle = NULL;
\r
9059 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9060 siStartInfo.cbReserved2 = 0;
\r
9061 siStartInfo.lpReserved2 = NULL;
\r
9062 siStartInfo.hStdInput = NULL;
\r
9063 siStartInfo.hStdOutput = NULL;
\r
9064 siStartInfo.hStdError = NULL;
\r
9066 CreateProcess(NULL,
\r
9067 cmdLine, /* command line */
\r
9068 NULL, /* process security attributes */
\r
9069 NULL, /* primary thread security attrs */
\r
9070 TRUE, /* handles are inherited */
\r
9071 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9072 NULL, /* use parent's environment */
\r
9074 &siStartInfo, /* STARTUPINFO pointer */
\r
9075 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9077 CloseHandle(piProcInfo.hThread);
\r
9080 /* Start a child process running the given program.
\r
9081 The process's standard output can be read from "from", and its
\r
9082 standard input can be written to "to".
\r
9083 Exit with fatal error if anything goes wrong.
\r
9084 Returns an opaque pointer that can be used to destroy the process
\r
9088 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
9090 #define BUFSIZE 4096
\r
9092 HANDLE hChildStdinRd, hChildStdinWr,
\r
9093 hChildStdoutRd, hChildStdoutWr;
\r
9094 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
9095 SECURITY_ATTRIBUTES saAttr;
\r
9097 PROCESS_INFORMATION piProcInfo;
\r
9098 STARTUPINFO siStartInfo;
\r
9100 char buf[MSG_SIZ];
\r
9103 if (appData.debugMode) {
\r
9104 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
9109 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
9110 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
9111 saAttr.bInheritHandle = TRUE;
\r
9112 saAttr.lpSecurityDescriptor = NULL;
\r
9115 * The steps for redirecting child's STDOUT:
\r
9116 * 1. Create anonymous pipe to be STDOUT for child.
\r
9117 * 2. Create a noninheritable duplicate of read handle,
\r
9118 * and close the inheritable read handle.
\r
9121 /* Create a pipe for the child's STDOUT. */
\r
9122 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
9123 return GetLastError();
\r
9126 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
9127 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
9128 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
9129 FALSE, /* not inherited */
\r
9130 DUPLICATE_SAME_ACCESS);
\r
9132 return GetLastError();
\r
9134 CloseHandle(hChildStdoutRd);
\r
9137 * The steps for redirecting child's STDIN:
\r
9138 * 1. Create anonymous pipe to be STDIN for child.
\r
9139 * 2. Create a noninheritable duplicate of write handle,
\r
9140 * and close the inheritable write handle.
\r
9143 /* Create a pipe for the child's STDIN. */
\r
9144 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
9145 return GetLastError();
\r
9148 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
9149 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
9150 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
9151 FALSE, /* not inherited */
\r
9152 DUPLICATE_SAME_ACCESS);
\r
9154 return GetLastError();
\r
9156 CloseHandle(hChildStdinWr);
\r
9158 /* Arrange to (1) look in dir for the child .exe file, and
\r
9159 * (2) have dir be the child's working directory. Interpret
\r
9160 * dir relative to the directory WinBoard loaded from. */
\r
9161 GetCurrentDirectory(MSG_SIZ, buf);
\r
9162 SetCurrentDirectory(installDir);
\r
9163 SetCurrentDirectory(dir);
\r
9165 /* Now create the child process. */
\r
9167 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9168 siStartInfo.lpReserved = NULL;
\r
9169 siStartInfo.lpDesktop = NULL;
\r
9170 siStartInfo.lpTitle = NULL;
\r
9171 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9172 siStartInfo.cbReserved2 = 0;
\r
9173 siStartInfo.lpReserved2 = NULL;
\r
9174 siStartInfo.hStdInput = hChildStdinRd;
\r
9175 siStartInfo.hStdOutput = hChildStdoutWr;
\r
9176 siStartInfo.hStdError = hChildStdoutWr;
\r
9178 fSuccess = CreateProcess(NULL,
\r
9179 cmdLine, /* command line */
\r
9180 NULL, /* process security attributes */
\r
9181 NULL, /* primary thread security attrs */
\r
9182 TRUE, /* handles are inherited */
\r
9183 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9184 NULL, /* use parent's environment */
\r
9186 &siStartInfo, /* STARTUPINFO pointer */
\r
9187 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9189 err = GetLastError();
\r
9190 SetCurrentDirectory(buf); /* return to prev directory */
\r
9195 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
9196 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
9197 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
9200 /* Close the handles we don't need in the parent */
\r
9201 CloseHandle(piProcInfo.hThread);
\r
9202 CloseHandle(hChildStdinRd);
\r
9203 CloseHandle(hChildStdoutWr);
\r
9205 /* Prepare return value */
\r
9206 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9207 cp->kind = CPReal;
\r
9208 cp->hProcess = piProcInfo.hProcess;
\r
9209 cp->pid = piProcInfo.dwProcessId;
\r
9210 cp->hFrom = hChildStdoutRdDup;
\r
9211 cp->hTo = hChildStdinWrDup;
\r
9213 *pr = (void *) cp;
\r
9215 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
9216 2000 where engines sometimes don't see the initial command(s)
\r
9217 from WinBoard and hang. I don't understand how that can happen,
\r
9218 but the Sleep is harmless, so I've put it in. Others have also
\r
9219 reported what may be the same problem, so hopefully this will fix
\r
9220 it for them too. */
\r
9228 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
9230 ChildProc *cp; int result;
\r
9232 cp = (ChildProc *) pr;
\r
9233 if (cp == NULL) return;
\r
9235 switch (cp->kind) {
\r
9237 /* TerminateProcess is considered harmful, so... */
\r
9238 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9239 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9240 /* The following doesn't work because the chess program
\r
9241 doesn't "have the same console" as WinBoard. Maybe
\r
9242 we could arrange for this even though neither WinBoard
\r
9243 nor the chess program uses a console for stdio? */
\r
9244 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9246 /* [AS] Special termination modes for misbehaving programs... */
\r
9247 if( signal & 8 ) {
\r
9248 result = TerminateProcess( cp->hProcess, 0 );
\r
9250 if ( appData.debugMode) {
\r
9251 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9254 else if( signal & 4 ) {
\r
9255 DWORD dw = WaitForSingleObject( cp->hProcess, appData.delayAfterQuit*1000 + 50 ); // Wait 3 seconds at most
\r
9257 if( dw != WAIT_OBJECT_0 ) {
\r
9258 result = TerminateProcess( cp->hProcess, 0 );
\r
9260 if ( appData.debugMode) {
\r
9261 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9267 CloseHandle(cp->hProcess);
\r
9271 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9275 closesocket(cp->sock);
\r
9280 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9281 closesocket(cp->sock);
\r
9282 closesocket(cp->sock2);
\r
9290 InterruptChildProcess(ProcRef pr)
\r
9294 cp = (ChildProc *) pr;
\r
9295 if (cp == NULL) return;
\r
9296 switch (cp->kind) {
\r
9298 /* The following doesn't work because the chess program
\r
9299 doesn't "have the same console" as WinBoard. Maybe
\r
9300 we could arrange for this even though neither WinBoard
\r
9301 nor the chess program uses a console for stdio */
\r
9302 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9307 /* Can't interrupt */
\r
9311 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9318 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9320 char cmdLine[MSG_SIZ];
\r
9322 if (port[0] == NULLCHAR) {
\r
9323 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9325 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9327 return StartChildProcess(cmdLine, "", pr);
\r
9331 /* Code to open TCP sockets */
\r
9334 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9340 struct sockaddr_in sa, mysa;
\r
9341 struct hostent FAR *hp;
\r
9342 unsigned short uport;
\r
9343 WORD wVersionRequested;
\r
9346 /* Initialize socket DLL */
\r
9347 wVersionRequested = MAKEWORD(1, 1);
\r
9348 err = WSAStartup(wVersionRequested, &wsaData);
\r
9349 if (err != 0) return err;
\r
9352 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9353 err = WSAGetLastError();
\r
9358 /* Bind local address using (mostly) don't-care values.
\r
9360 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9361 mysa.sin_family = AF_INET;
\r
9362 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9363 uport = (unsigned short) 0;
\r
9364 mysa.sin_port = htons(uport);
\r
9365 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9366 == SOCKET_ERROR) {
\r
9367 err = WSAGetLastError();
\r
9372 /* Resolve remote host name */
\r
9373 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9374 if (!(hp = gethostbyname(host))) {
\r
9375 unsigned int b0, b1, b2, b3;
\r
9377 err = WSAGetLastError();
\r
9379 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9380 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9381 hp->h_addrtype = AF_INET;
\r
9383 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9384 hp->h_addr_list[0] = (char *) malloc(4);
\r
9385 hp->h_addr_list[0][0] = (char) b0;
\r
9386 hp->h_addr_list[0][1] = (char) b1;
\r
9387 hp->h_addr_list[0][2] = (char) b2;
\r
9388 hp->h_addr_list[0][3] = (char) b3;
\r
9394 sa.sin_family = hp->h_addrtype;
\r
9395 uport = (unsigned short) atoi(port);
\r
9396 sa.sin_port = htons(uport);
\r
9397 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9399 /* Make connection */
\r
9400 if (connect(s, (struct sockaddr *) &sa,
\r
9401 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9402 err = WSAGetLastError();
\r
9407 /* Prepare return value */
\r
9408 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9409 cp->kind = CPSock;
\r
9411 *pr = (ProcRef *) cp;
\r
9417 OpenCommPort(char *name, ProcRef *pr)
\r
9422 char fullname[MSG_SIZ];
\r
9424 if (*name != '\\')
\r
9425 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9427 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9429 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9430 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9431 if (h == (HANDLE) -1) {
\r
9432 return GetLastError();
\r
9436 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9438 /* Accumulate characters until a 100ms pause, then parse */
\r
9439 ct.ReadIntervalTimeout = 100;
\r
9440 ct.ReadTotalTimeoutMultiplier = 0;
\r
9441 ct.ReadTotalTimeoutConstant = 0;
\r
9442 ct.WriteTotalTimeoutMultiplier = 0;
\r
9443 ct.WriteTotalTimeoutConstant = 0;
\r
9444 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9446 /* Prepare return value */
\r
9447 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9448 cp->kind = CPComm;
\r
9451 *pr = (ProcRef *) cp;
\r
9457 OpenLoopback(ProcRef *pr)
\r
9459 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9465 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9470 struct sockaddr_in sa, mysa;
\r
9471 struct hostent FAR *hp;
\r
9472 unsigned short uport;
\r
9473 WORD wVersionRequested;
\r
9476 char stderrPortStr[MSG_SIZ];
\r
9478 /* Initialize socket DLL */
\r
9479 wVersionRequested = MAKEWORD(1, 1);
\r
9480 err = WSAStartup(wVersionRequested, &wsaData);
\r
9481 if (err != 0) return err;
\r
9483 /* Resolve remote host name */
\r
9484 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9485 if (!(hp = gethostbyname(host))) {
\r
9486 unsigned int b0, b1, b2, b3;
\r
9488 err = WSAGetLastError();
\r
9490 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9491 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9492 hp->h_addrtype = AF_INET;
\r
9494 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9495 hp->h_addr_list[0] = (char *) malloc(4);
\r
9496 hp->h_addr_list[0][0] = (char) b0;
\r
9497 hp->h_addr_list[0][1] = (char) b1;
\r
9498 hp->h_addr_list[0][2] = (char) b2;
\r
9499 hp->h_addr_list[0][3] = (char) b3;
\r
9505 sa.sin_family = hp->h_addrtype;
\r
9506 uport = (unsigned short) 514;
\r
9507 sa.sin_port = htons(uport);
\r
9508 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9510 /* Bind local socket to unused "privileged" port address
\r
9512 s = INVALID_SOCKET;
\r
9513 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9514 mysa.sin_family = AF_INET;
\r
9515 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9516 for (fromPort = 1023;; fromPort--) {
\r
9517 if (fromPort < 0) {
\r
9519 return WSAEADDRINUSE;
\r
9521 if (s == INVALID_SOCKET) {
\r
9522 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9523 err = WSAGetLastError();
\r
9528 uport = (unsigned short) fromPort;
\r
9529 mysa.sin_port = htons(uport);
\r
9530 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9531 == SOCKET_ERROR) {
\r
9532 err = WSAGetLastError();
\r
9533 if (err == WSAEADDRINUSE) continue;
\r
9537 if (connect(s, (struct sockaddr *) &sa,
\r
9538 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9539 err = WSAGetLastError();
\r
9540 if (err == WSAEADDRINUSE) {
\r
9551 /* Bind stderr local socket to unused "privileged" port address
\r
9553 s2 = INVALID_SOCKET;
\r
9554 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9555 mysa.sin_family = AF_INET;
\r
9556 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9557 for (fromPort = 1023;; fromPort--) {
\r
9558 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9559 if (fromPort < 0) {
\r
9560 (void) closesocket(s);
\r
9562 return WSAEADDRINUSE;
\r
9564 if (s2 == INVALID_SOCKET) {
\r
9565 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9566 err = WSAGetLastError();
\r
9572 uport = (unsigned short) fromPort;
\r
9573 mysa.sin_port = htons(uport);
\r
9574 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9575 == SOCKET_ERROR) {
\r
9576 err = WSAGetLastError();
\r
9577 if (err == WSAEADDRINUSE) continue;
\r
9578 (void) closesocket(s);
\r
9582 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9583 err = WSAGetLastError();
\r
9584 if (err == WSAEADDRINUSE) {
\r
9586 s2 = INVALID_SOCKET;
\r
9589 (void) closesocket(s);
\r
9590 (void) closesocket(s2);
\r
9596 prevStderrPort = fromPort; // remember port used
\r
9597 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9599 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9600 err = WSAGetLastError();
\r
9601 (void) closesocket(s);
\r
9602 (void) closesocket(s2);
\r
9607 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9608 err = WSAGetLastError();
\r
9609 (void) closesocket(s);
\r
9610 (void) closesocket(s2);
\r
9614 if (*user == NULLCHAR) user = UserName();
\r
9615 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9616 err = WSAGetLastError();
\r
9617 (void) closesocket(s);
\r
9618 (void) closesocket(s2);
\r
9622 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9623 err = WSAGetLastError();
\r
9624 (void) closesocket(s);
\r
9625 (void) closesocket(s2);
\r
9630 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9631 err = WSAGetLastError();
\r
9632 (void) closesocket(s);
\r
9633 (void) closesocket(s2);
\r
9637 (void) closesocket(s2); /* Stop listening */
\r
9639 /* Prepare return value */
\r
9640 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9641 cp->kind = CPRcmd;
\r
9644 *pr = (ProcRef *) cp;
\r
9651 AddInputSource(ProcRef pr, int lineByLine,
\r
9652 InputCallback func, VOIDSTAR closure)
\r
9654 InputSource *is, *is2 = NULL;
\r
9655 ChildProc *cp = (ChildProc *) pr;
\r
9657 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9658 is->lineByLine = lineByLine;
\r
9660 is->closure = closure;
\r
9661 is->second = NULL;
\r
9662 is->next = is->buf;
\r
9663 if (pr == NoProc) {
\r
9664 is->kind = CPReal;
\r
9665 consoleInputSource = is;
\r
9667 is->kind = cp->kind;
\r
9669 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9670 we create all threads suspended so that the is->hThread variable can be
\r
9671 safely assigned, then let the threads start with ResumeThread.
\r
9673 switch (cp->kind) {
\r
9675 is->hFile = cp->hFrom;
\r
9676 cp->hFrom = NULL; /* now owned by InputThread */
\r
9678 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9679 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9683 is->hFile = cp->hFrom;
\r
9684 cp->hFrom = NULL; /* now owned by InputThread */
\r
9686 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9687 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9691 is->sock = cp->sock;
\r
9693 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9694 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9698 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9700 is->sock = cp->sock;
\r
9702 is2->sock = cp->sock2;
\r
9703 is2->second = is2;
\r
9705 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9706 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9708 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9709 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9713 if( is->hThread != NULL ) {
\r
9714 ResumeThread( is->hThread );
\r
9717 if( is2 != NULL && is2->hThread != NULL ) {
\r
9718 ResumeThread( is2->hThread );
\r
9722 return (InputSourceRef) is;
\r
9726 RemoveInputSource(InputSourceRef isr)
\r
9730 is = (InputSource *) isr;
\r
9731 is->hThread = NULL; /* tell thread to stop */
\r
9732 CloseHandle(is->hThread);
\r
9733 if (is->second != NULL) {
\r
9734 is->second->hThread = NULL;
\r
9735 CloseHandle(is->second->hThread);
\r
9739 int no_wrap(char *message, int count)
\r
9741 ConsoleOutput(message, count, FALSE);
\r
9746 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9749 int outCount = SOCKET_ERROR;
\r
9750 ChildProc *cp = (ChildProc *) pr;
\r
9751 static OVERLAPPED ovl;
\r
9752 static int line = 0;
\r
9756 if (appData.noJoin || !appData.useInternalWrap)
\r
9757 return no_wrap(message, count);
\r
9760 int width = get_term_width();
\r
9761 int len = wrap(NULL, message, count, width, &line);
\r
9762 char *msg = malloc(len);
\r
9766 return no_wrap(message, count);
\r
9769 dbgchk = wrap(msg, message, count, width, &line);
\r
9770 if (dbgchk != len && appData.debugMode)
\r
9771 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9772 ConsoleOutput(msg, len, FALSE);
\r
9779 if (ovl.hEvent == NULL) {
\r
9780 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9782 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9784 switch (cp->kind) {
\r
9787 outCount = send(cp->sock, message, count, 0);
\r
9788 if (outCount == SOCKET_ERROR) {
\r
9789 *outError = WSAGetLastError();
\r
9791 *outError = NO_ERROR;
\r
9796 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9797 &dOutCount, NULL)) {
\r
9798 *outError = NO_ERROR;
\r
9799 outCount = (int) dOutCount;
\r
9801 *outError = GetLastError();
\r
9806 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9807 &dOutCount, &ovl);
\r
9808 if (*outError == NO_ERROR) {
\r
9809 outCount = (int) dOutCount;
\r
9819 if(n != 0) Sleep(n);
\r
9823 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9826 /* Ignore delay, not implemented for WinBoard */
\r
9827 return OutputToProcess(pr, message, count, outError);
\r
9832 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9833 char *buf, int count, int error)
\r
9835 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9838 /* see wgamelist.c for Game List functions */
\r
9839 /* see wedittags.c for Edit Tags functions */
\r
9846 char buf[MSG_SIZ];
\r
9849 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9850 f = fopen(buf, "r");
\r
9852 ProcessICSInitScript(f);
\r
9862 StartAnalysisClock()
\r
9864 if (analysisTimerEvent) return;
\r
9865 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9866 (UINT) 2000, NULL);
\r
9870 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9872 highlightInfo.sq[0].x = fromX;
\r
9873 highlightInfo.sq[0].y = fromY;
\r
9874 highlightInfo.sq[1].x = toX;
\r
9875 highlightInfo.sq[1].y = toY;
\r
9881 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9882 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9886 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9888 premoveHighlightInfo.sq[0].x = fromX;
\r
9889 premoveHighlightInfo.sq[0].y = fromY;
\r
9890 premoveHighlightInfo.sq[1].x = toX;
\r
9891 premoveHighlightInfo.sq[1].y = toY;
\r
9895 ClearPremoveHighlights()
\r
9897 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9898 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9902 ShutDownFrontEnd()
\r
9904 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9905 DeleteClipboardTempFiles();
\r
9911 if (IsIconic(hwndMain))
\r
9912 ShowWindow(hwndMain, SW_RESTORE);
\r
9914 SetActiveWindow(hwndMain);
\r
9918 * Prototypes for animation support routines
\r
9920 static void ScreenSquare(int column, int row, POINT * pt);
\r
9921 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9922 POINT frames[], int * nFrames);
\r
9928 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9929 { // [HGM] atomic: animate blast wave
\r
9932 explodeInfo.fromX = fromX;
\r
9933 explodeInfo.fromY = fromY;
\r
9934 explodeInfo.toX = toX;
\r
9935 explodeInfo.toY = toY;
\r
9936 for(i=1; i<4*kFactor; i++) {
\r
9937 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9938 DrawPosition(FALSE, board);
\r
9939 Sleep(appData.animSpeed);
\r
9941 explodeInfo.radius = 0;
\r
9942 DrawPosition(TRUE, board);
\r
9946 AnimateMove(board, fromX, fromY, toX, toY)
\r
9953 ChessSquare piece;
\r
9954 int x = toX, y = toY;
\r
9955 POINT start, finish, mid;
\r
9956 POINT frames[kFactor * 2 + 1];
\r
9959 if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();
\r
9961 if (!appData.animate) return;
\r
9962 if (doingSizing) return;
\r
9963 if (fromY < 0 || fromX < 0) return;
\r
9964 piece = board[fromY][fromX];
\r
9965 if (piece >= EmptySquare) return;
\r
9967 if(killX >= 0) toX = killX, toY = killY; // [HGM] lion: first to kill square
\r
9971 ScreenSquare(fromX, fromY, &start);
\r
9972 ScreenSquare(toX, toY, &finish);
\r
9974 /* All moves except knight jumps move in straight line */
\r
9975 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9976 mid.x = start.x + (finish.x - start.x) / 2;
\r
9977 mid.y = start.y + (finish.y - start.y) / 2;
\r
9979 /* Knight: make straight movement then diagonal */
\r
9980 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9981 mid.x = start.x + (finish.x - start.x) / 2;
\r
9985 mid.y = start.y + (finish.y - start.y) / 2;
\r
9989 /* Don't use as many frames for very short moves */
\r
9990 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9991 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9993 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9995 animInfo.from.x = fromX;
\r
9996 animInfo.from.y = fromY;
\r
9997 animInfo.to.x = toX;
\r
9998 animInfo.to.y = toY;
\r
9999 animInfo.lastpos = start;
\r
10000 animInfo.piece = piece;
\r
10001 for (n = 0; n < nFrames; n++) {
\r
10002 animInfo.pos = frames[n];
\r
10003 DrawPosition(FALSE, NULL);
\r
10004 animInfo.lastpos = animInfo.pos;
\r
10005 Sleep(appData.animSpeed);
\r
10007 animInfo.pos = finish;
\r
10008 DrawPosition(FALSE, NULL);
\r
10010 if(toX != x || toY != y) { fromX = toX; fromY = toY; toX = x; toY = y; goto again; } // second leg
\r
10012 animInfo.piece = EmptySquare;
\r
10013 Explode(board, fromX, fromY, toX, toY);
\r
10016 /* Convert board position to corner of screen rect and color */
\r
10019 ScreenSquare(column, row, pt)
\r
10020 int column; int row; POINT * pt;
\r
10023 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
10024 pt->y = lineGap + row * (squareSize + lineGap) + border;
\r
10026 pt->x = lineGap + column * (squareSize + lineGap) + border;
\r
10027 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
10031 /* Generate a series of frame coords from start->mid->finish.
\r
10032 The movement rate doubles until the half way point is
\r
10033 reached, then halves back down to the final destination,
\r
10034 which gives a nice slow in/out effect. The algorithmn
\r
10035 may seem to generate too many intermediates for short
\r
10036 moves, but remember that the purpose is to attract the
\r
10037 viewers attention to the piece about to be moved and
\r
10038 then to where it ends up. Too few frames would be less
\r
10042 Tween(start, mid, finish, factor, frames, nFrames)
\r
10043 POINT * start; POINT * mid;
\r
10044 POINT * finish; int factor;
\r
10045 POINT frames[]; int * nFrames;
\r
10047 int n, fraction = 1, count = 0;
\r
10049 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
10050 for (n = 0; n < factor; n++)
\r
10052 for (n = 0; n < factor; n++) {
\r
10053 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
10054 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
10056 fraction = fraction / 2;
\r
10060 frames[count] = *mid;
\r
10063 /* Slow out, stepping 1/2, then 1/4, ... */
\r
10065 for (n = 0; n < factor; n++) {
\r
10066 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
10067 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
10069 fraction = fraction * 2;
\r
10071 *nFrames = count;
\r
10075 SettingsPopUp(ChessProgramState *cps)
\r
10076 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
10077 EngineOptionsPopup(savedHwnd, cps);
\r
10080 int flock(int fid, int code)
\r
10082 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
10084 ov.hEvent = NULL;
\r
10086 ov.OffsetHigh = 0;
\r
10088 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
10090 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
10091 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
10092 default: return -1;
\r
10101 static char col[8][20];
\r
10102 COLORREF color = *(COLORREF *) colorVariable[n];
\r
10104 snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
10109 ActivateTheme (int new)
\r
10110 { // Redo initialization of features depending on options that can occur in themes
\r
10112 if(new) InitDrawingColors();
\r
10113 fontBitmapSquareSize = 0; // request creation of new font pieces
\r
10114 InitDrawingSizes(boardSize, 0);
\r
10115 InvalidateRect(hwndMain, NULL, TRUE);
\r