2 * WinBoard.c -- Windows NT front end to XBoard
\r
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
\r
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
\r
8 * 2007, 2008, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
\r
10 * Enhancements Copyright 2005 Alessandro Scotti
\r
12 * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,
\r
13 * which was written and is copyrighted by Wayne Christopher.
\r
15 * The following terms apply to Digital Equipment Corporation's copyright
\r
16 * interest in XBoard:
\r
17 * ------------------------------------------------------------------------
\r
18 * All Rights Reserved
\r
20 * Permission to use, copy, modify, and distribute this software and its
\r
21 * documentation for any purpose and without fee is hereby granted,
\r
22 * provided that the above copyright notice appear in all copies and that
\r
23 * both that copyright notice and this permission notice appear in
\r
24 * supporting documentation, and that the name of Digital not be
\r
25 * used in advertising or publicity pertaining to distribution of the
\r
26 * software without specific, written prior permission.
\r
28 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
\r
29 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
\r
30 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
\r
31 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
\r
32 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
\r
33 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
\r
35 * ------------------------------------------------------------------------
\r
37 * The following terms apply to the enhanced version of XBoard
\r
38 * distributed by the Free Software Foundation:
\r
39 * ------------------------------------------------------------------------
\r
41 * GNU XBoard is free software: you can redistribute it and/or modify
\r
42 * it under the terms of the GNU General Public License as published by
\r
43 * the Free Software Foundation, either version 3 of the License, or (at
\r
44 * your option) any later version.
\r
46 * GNU XBoard is distributed in the hope that it will be useful, but
\r
47 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
48 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
49 * General Public License for more details.
\r
51 * You should have received a copy of the GNU General Public License
\r
52 * along with this program. If not, see http://www.gnu.org/licenses/. *
\r
54 *------------------------------------------------------------------------
\r
55 ** See the file ChangeLog for a revision history. */
\r
59 #include <windows.h>
\r
60 #include <winuser.h>
\r
61 #include <winsock.h>
\r
62 #include <commctrl.h>
\r
68 #include <sys/stat.h>
\r
71 #include <commdlg.h>
\r
73 #include <richedit.h>
\r
74 #include <mmsystem.h>
\r
84 #include "frontend.h"
\r
85 #include "backend.h"
\r
86 #include "winboard.h"
\r
88 #include "wclipbrd.h"
\r
89 #include "woptions.h"
\r
90 #include "wsockerr.h"
\r
91 #include "defaults.h"
\r
95 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
\r
98 void mysrandom(unsigned int seed);
\r
100 extern int whiteFlag, blackFlag;
\r
101 Boolean flipClock = FALSE;
\r
102 extern HANDLE chatHandle[];
\r
103 extern enum ICS_TYPE ics_type;
\r
105 int MySearchPath P((char *installDir, char *name, char *fullname));
\r
106 int MyGetFullPathName P((char *name, char *fullname));
\r
107 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
108 VOID NewVariantPopup(HWND hwnd);
\r
109 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
110 /*char*/int promoChar));
\r
111 void DisplayMove P((int moveNumber));
\r
112 void ChatPopUp P((char *s));
\r
114 ChessSquare piece;
\r
115 POINT pos; /* window coordinates of current pos */
\r
116 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
117 POINT from; /* board coordinates of the piece's orig pos */
\r
118 POINT to; /* board coordinates of the piece's new pos */
\r
121 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
124 POINT start; /* window coordinates of start pos */
\r
125 POINT pos; /* window coordinates of current pos */
\r
126 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
127 POINT from; /* board coordinates of the piece's orig pos */
\r
131 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };
\r
134 POINT sq[2]; /* board coordinates of from, to squares */
\r
137 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
138 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
139 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
140 static HighlightInfo oldPartnerHighlight = { {{-1, -1}, {-1, -1}} };
\r
142 typedef struct { // [HGM] atomic
\r
143 int fromX, fromY, toX, toY, radius;
\r
146 static ExplodeInfo explodeInfo;
\r
148 /* Window class names */
\r
149 char szAppName[] = "WinBoard";
\r
150 char szConsoleName[] = "WBConsole";
\r
152 /* Title bar text */
\r
153 char szTitle[] = "WinBoard";
\r
154 char szConsoleTitle[] = "I C S Interaction";
\r
157 char *settingsFileName;
\r
158 Boolean saveSettingsOnExit;
\r
159 char installDir[MSG_SIZ];
\r
160 int errorExitStatus;
\r
162 BoardSize boardSize;
\r
163 Boolean chessProgram;
\r
164 //static int boardX, boardY;
\r
165 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
166 int squareSize, lineGap, minorSize, border;
\r
167 static int winW, winH;
\r
168 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
169 static int logoHeight = 0;
\r
170 static char messageText[MESSAGE_TEXT_MAX];
\r
171 static int clockTimerEvent = 0;
\r
172 static int loadGameTimerEvent = 0;
\r
173 static int analysisTimerEvent = 0;
\r
174 static DelayedEventCallback delayedTimerCallback;
\r
175 static int delayedTimerEvent = 0;
\r
176 static int buttonCount = 2;
\r
177 char *icsTextMenuString;
\r
179 char *firstChessProgramNames;
\r
180 char *secondChessProgramNames;
\r
182 #define PALETTESIZE 256
\r
184 HINSTANCE hInst; /* current instance */
\r
185 Boolean alwaysOnTop = FALSE;
\r
187 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
188 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
189 COLORREF markerColor[8] = { 0x00FFFF, 0x0000FF, 0x00FF00, 0xFF0000, 0xFFFF00, 0xFF00FF, 0xFFFFFF, 0x000000 };
\r
191 ColorClass currentColorClass;
\r
193 static HWND savedHwnd;
\r
194 HWND hCommPort = NULL; /* currently open comm port */
\r
195 static HWND hwndPause; /* pause button */
\r
196 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
197 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
198 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
199 explodeBrush, /* [HGM] atomic */
\r
200 markerBrush[8], /* [HGM] markers */
\r
201 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
202 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
203 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
204 static HPEN gridPen = NULL;
\r
205 static HPEN highlightPen = NULL;
\r
206 static HPEN premovePen = NULL;
\r
207 static NPLOGPALETTE pLogPal;
\r
208 static BOOL paletteChanged = FALSE;
\r
209 static HICON iconWhite, iconBlack, iconCurrent;
\r
210 static int doingSizing = FALSE;
\r
211 static int lastSizing = 0;
\r
212 static int prevStderrPort;
\r
213 static HBITMAP userLogo;
\r
215 static HBITMAP liteBackTexture = NULL;
\r
216 static HBITMAP darkBackTexture = NULL;
\r
217 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
218 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
219 static int backTextureSquareSize = 0;
\r
220 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
222 #if __GNUC__ && !defined(_winmajor)
\r
223 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
226 #if defined(_winmajor)
\r
227 #define oldDialog (_winmajor < 4)
\r
229 #define oldDialog 0
\r
233 #define INTERNATIONAL
\r
235 #ifdef INTERNATIONAL
\r
236 # define _(s) T_(s)
\r
242 # define Translate(x, y)
\r
243 # define LoadLanguageFile(s)
\r
246 #ifdef INTERNATIONAL
\r
248 Boolean barbaric; // flag indicating if translation is needed
\r
250 // list of item numbers used in each dialog (used to alter language at run time)
\r
252 #define ABOUTBOX -1 /* not sure why these are needed */
\r
253 #define ABOUTBOX2 -1
\r
255 int dialogItems[][42] = {
\r
256 { ABOUTBOX, IDOK, OPT_MESS, 400 },
\r
257 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed,
\r
258 OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors, IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL },
\r
259 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,
\r
260 OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, IDOK, IDCANCEL },
\r
261 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
262 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
263 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
264 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
265 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
266 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
267 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
268 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
269 { ABOUTBOX2, IDC_ChessBoard },
\r
270 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
271 OPT_GameListClose, IDC_GameListDoFilter },
\r
272 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
273 { DLG_Error, IDOK },
\r
274 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
275 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
276 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
277 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
278 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
279 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
280 { DLG_IndexNumber, IDC_Index },
\r
281 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
282 { DLG_TypeInName, IDOK, IDCANCEL },
\r
283 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
284 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
285 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
286 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
287 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
288 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
289 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
290 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
291 OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove },
\r
292 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
293 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
294 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
295 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
296 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
297 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
298 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
299 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
300 GPB_General, GPB_Alarm, OPT_AutoCreate },
\r
301 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
302 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
303 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
304 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
305 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
306 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
307 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
308 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid },
\r
309 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
310 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
311 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
312 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
313 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
314 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
315 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,
\r
316 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
317 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
318 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
319 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
320 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,
\r
321 OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7,
\r
322 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
323 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
324 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
325 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
326 { DLG_MoveHistory },
\r
327 { DLG_EvalGraph },
\r
328 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
329 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
330 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
331 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
332 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
333 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
334 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
335 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
336 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
340 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];
\r
341 static int lastChecked;
\r
342 static char oldLanguage[MSG_SIZ], *menuText[10][30];
\r
343 extern int tinyLayout;
\r
344 extern char * menuBarText[][10];
\r
347 LoadLanguageFile(char *name)
\r
348 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
350 int i=0, j=0, n=0, k;
\r
353 if(!name || name[0] == NULLCHAR) return;
\r
354 snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension
\r
355 appData.language = oldLanguage;
\r
356 if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on
\r
357 if((f = fopen(buf, "r")) == NULL) return;
\r
358 while((k = fgetc(f)) != EOF) {
\r
359 if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }
\r
360 languageBuf[i] = k;
\r
362 if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {
\r
364 if(p = strstr(languageBuf + n + 1, "\" === \"")) {
\r
365 if(p > languageBuf+n+2 && p+8 < languageBuf+i) {
\r
366 if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }
\r
367 english[j] = languageBuf + n + 1; *p = 0;
\r
368 foreign[j++] = p + 7; languageBuf[i-1] = 0;
\r
369 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);
\r
374 } else if(i > 0 && languageBuf[i-1] == '\\') {
\r
376 case 'n': k = '\n'; break;
\r
377 case 'r': k = '\r'; break;
\r
378 case 't': k = '\t'; break;
\r
380 languageBuf[--i] = k;
\r
385 barbaric = (j != 0);
\r
386 safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );
\r
391 { // return the translation of the given string
\r
392 // efficiency can be improved a lot...
\r
394 static char buf[MSG_SIZ];
\r
395 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
396 if(!barbaric) return s;
\r
397 if(!s) return ""; // sanity
\r
398 while(english[i]) {
\r
399 if(!strcmp(s, english[i])) return foreign[i];
\r
400 if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending
\r
401 snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion
\r
410 Translate(HWND hDlg, int dialogID)
\r
411 { // translate all text items in the given dialog
\r
413 char buf[MSG_SIZ], *s;
\r
414 if(!barbaric) return;
\r
415 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
416 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
417 GetWindowText( hDlg, buf, MSG_SIZ );
\r
419 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
420 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
421 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
422 if(strlen(buf) == 0) continue;
\r
424 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
429 TranslateOneMenu(int i, HMENU subMenu)
\r
432 static MENUITEMINFO info;
\r
434 info.cbSize = sizeof(MENUITEMINFO);
\r
435 info.fMask = MIIM_STATE | MIIM_TYPE;
\r
436 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
438 info.dwTypeData = buf;
\r
439 info.cch = sizeof(buf);
\r
440 GetMenuItemInfo(subMenu, j, TRUE, &info);
\r
442 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );
\r
443 else menuText[i][j] = strdup(buf); // remember original on first change
\r
445 if(buf[0] == NULLCHAR) continue;
\r
446 info.dwTypeData = T_(buf);
\r
447 info.cch = strlen(buf)+1;
\r
448 SetMenuItemInfo(subMenu, j, TRUE, &info);
\r
454 TranslateMenus(int addLanguage)
\r
457 WIN32_FIND_DATA fileData;
\r
459 #define IDM_English 1970
\r
461 HMENU mainMenu = GetMenu(hwndMain);
\r
462 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
463 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
464 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
465 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
466 TranslateOneMenu(i, subMenu);
\r
468 DrawMenuBar(hwndMain);
\r
471 if(!addLanguage) return;
\r
472 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
473 HMENU mainMenu = GetMenu(hwndMain);
\r
474 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
475 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
476 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
477 i = 0; lastChecked = IDM_English;
\r
479 char *p, *q = fileData.cFileName;
\r
480 int checkFlag = MF_UNCHECKED;
\r
481 languageFile[i] = strdup(q);
\r
482 if(barbaric && !strcmp(oldLanguage, q)) {
\r
483 checkFlag = MF_CHECKED;
\r
484 lastChecked = IDM_English + i + 1;
\r
485 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
487 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
488 p = strstr(fileData.cFileName, ".lng");
\r
490 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
491 } while(FindNextFile(hFind, &fileData));
\r
498 #define IDM_RecentEngines 3000
\r
501 RecentEngineMenu (char *s)
\r
503 if(appData.icsActive) return;
\r
504 if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty
\r
505 HMENU mainMenu = GetMenu(hwndMain);
\r
506 HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu
\r
507 int i=IDM_RecentEngines;
\r
508 recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu
\r
509 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
511 char *p = strchr(s, '\n');
\r
512 if(p == NULL) return; // malformed!
\r
514 AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);
\r
528 int cliWidth, cliHeight;
\r
531 SizeInfo sizeInfo[] =
\r
533 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
534 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
535 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
536 { "petite", 33, 1, 1, 1, 0, 0 },
\r
537 { "slim", 37, 2, 1, 0, 0, 0 },
\r
538 { "small", 40, 2, 1, 0, 0, 0 },
\r
539 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
540 { "middling", 49, 2, 0, 0, 0, 0 },
\r
541 { "average", 54, 2, 0, 0, 0, 0 },
\r
542 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
543 { "medium", 64, 3, 0, 0, 0, 0 },
\r
544 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
545 { "large", 80, 3, 0, 0, 0, 0 },
\r
546 { "big", 87, 3, 0, 0, 0, 0 },
\r
547 { "huge", 95, 3, 0, 0, 0, 0 },
\r
548 { "giant", 108, 3, 0, 0, 0, 0 },
\r
549 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
550 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
551 { NULL, 0, 0, 0, 0, 0, 0 }
\r
554 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
555 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
557 { 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
558 { 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
559 { 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
560 { 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
561 { 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
562 { 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
563 { 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
564 { 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
565 { 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
566 { 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
567 { 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
568 { 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
569 { 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
570 { 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
571 { 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
572 { 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
573 { 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
574 { 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
577 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
586 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
587 #define N_BUTTONS 5
\r
589 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
591 {"<<", IDM_ToStart, NULL, NULL},
\r
592 {"<", IDM_Backward, NULL, NULL},
\r
593 {"P", IDM_Pause, NULL, NULL},
\r
594 {">", IDM_Forward, NULL, NULL},
\r
595 {">>", IDM_ToEnd, NULL, NULL},
\r
598 int tinyLayout = 0, smallLayout = 0;
\r
599 #define MENU_BAR_ITEMS 9
\r
600 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
601 { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },
\r
602 { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },
\r
606 MySound sounds[(int)NSoundClasses];
\r
607 MyTextAttribs textAttribs[(int)NColorClasses];
\r
609 MyColorizeAttribs colorizeAttribs[] = {
\r
610 { (COLORREF)0, 0, N_("Shout Text") },
\r
611 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
612 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
613 { (COLORREF)0, 0, N_("Channel Text") },
\r
614 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
615 { (COLORREF)0, 0, N_("Tell Text") },
\r
616 { (COLORREF)0, 0, N_("Challenge Text") },
\r
617 { (COLORREF)0, 0, N_("Request Text") },
\r
618 { (COLORREF)0, 0, N_("Seek Text") },
\r
619 { (COLORREF)0, 0, N_("Normal Text") },
\r
620 { (COLORREF)0, 0, N_("None") }
\r
625 static char *commentTitle;
\r
626 static char *commentText;
\r
627 static int commentIndex;
\r
628 static Boolean editComment = FALSE;
\r
631 char errorTitle[MSG_SIZ];
\r
632 char errorMessage[2*MSG_SIZ];
\r
633 HWND errorDialog = NULL;
\r
634 BOOLEAN moveErrorMessageUp = FALSE;
\r
635 BOOLEAN consoleEcho = TRUE;
\r
636 CHARFORMAT consoleCF;
\r
637 COLORREF consoleBackgroundColor;
\r
639 char *programVersion;
\r
645 typedef int CPKind;
\r
654 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
657 #define INPUT_SOURCE_BUF_SIZE 4096
\r
659 typedef struct _InputSource {
\r
666 char buf[INPUT_SOURCE_BUF_SIZE];
\r
670 InputCallback func;
\r
671 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
675 InputSource *consoleInputSource;
\r
680 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
681 VOID ConsoleCreate();
\r
683 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
684 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
685 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
686 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
688 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
689 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
690 void ParseIcsTextMenu(char *icsTextMenuString);
\r
691 VOID PopUpNameDialog(char firstchar);
\r
692 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
696 int GameListOptions();
\r
698 int dummy; // [HGM] for obsolete args
\r
700 HWND hwndMain = NULL; /* root window*/
\r
701 HWND hwndConsole = NULL;
\r
702 HWND commentDialog = NULL;
\r
703 HWND moveHistoryDialog = NULL;
\r
704 HWND evalGraphDialog = NULL;
\r
705 HWND engineOutputDialog = NULL;
\r
706 HWND gameListDialog = NULL;
\r
707 HWND editTagsDialog = NULL;
\r
709 int commentUp = FALSE;
\r
711 WindowPlacement wpMain;
\r
712 WindowPlacement wpConsole;
\r
713 WindowPlacement wpComment;
\r
714 WindowPlacement wpMoveHistory;
\r
715 WindowPlacement wpEvalGraph;
\r
716 WindowPlacement wpEngineOutput;
\r
717 WindowPlacement wpGameList;
\r
718 WindowPlacement wpTags;
\r
720 VOID EngineOptionsPopup(); // [HGM] settings
\r
722 VOID GothicPopUp(char *title, VariantClass variant);
\r
724 * Setting "frozen" should disable all user input other than deleting
\r
725 * the window. We do this while engines are initializing themselves.
\r
727 static int frozen = 0;
\r
728 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
734 if (frozen) return;
\r
736 hmenu = GetMenu(hwndMain);
\r
737 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
738 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
740 DrawMenuBar(hwndMain);
\r
743 /* Undo a FreezeUI */
\r
749 if (!frozen) return;
\r
751 hmenu = GetMenu(hwndMain);
\r
752 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
753 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
755 DrawMenuBar(hwndMain);
\r
758 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
760 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
766 #define JAWS_ALT_INTERCEPT
\r
767 #define JAWS_KBUP_NAVIGATION
\r
768 #define JAWS_KBDOWN_NAVIGATION
\r
769 #define JAWS_MENU_ITEMS
\r
770 #define JAWS_SILENCE
\r
771 #define JAWS_REPLAY
\r
773 #define JAWS_COPYRIGHT
\r
774 #define JAWS_DELETE(X) X
\r
775 #define SAYMACHINEMOVE()
\r
779 /*---------------------------------------------------------------------------*\
\r
783 \*---------------------------------------------------------------------------*/
\r
786 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
787 LPSTR lpCmdLine, int nCmdShow)
\r
790 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
791 // INITCOMMONCONTROLSEX ex;
\r
795 LoadLibrary("RICHED32.DLL");
\r
796 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
798 if (!InitApplication(hInstance)) {
\r
801 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
808 // InitCommonControlsEx(&ex);
\r
809 InitCommonControls();
\r
811 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
812 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
813 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
815 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
817 while (GetMessage(&msg, /* message structure */
\r
818 NULL, /* handle of window receiving the message */
\r
819 0, /* lowest message to examine */
\r
820 0)) /* highest message to examine */
\r
823 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
824 // [HGM] navigate: switch between all windows with tab
\r
825 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
826 int i, currentElement = 0;
\r
828 // first determine what element of the chain we come from (if any)
\r
829 if(appData.icsActive) {
\r
830 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
831 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
833 if(engineOutputDialog && EngineOutputIsUp()) {
\r
834 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
835 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
837 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
838 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
840 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
841 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
842 if(msg.hwnd == e1) currentElement = 2; else
\r
843 if(msg.hwnd == e2) currentElement = 3; else
\r
844 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
845 if(msg.hwnd == mh) currentElement = 4; else
\r
846 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
847 if(msg.hwnd == hText) currentElement = 5; else
\r
848 if(msg.hwnd == hInput) currentElement = 6; else
\r
849 for (i = 0; i < N_BUTTONS; i++) {
\r
850 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
853 // determine where to go to
\r
854 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
856 currentElement = (currentElement + direction) % 7;
\r
857 switch(currentElement) {
\r
859 h = hwndMain; break; // passing this case always makes the loop exit
\r
861 h = buttonDesc[0].hwnd; break; // could be NULL
\r
863 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
866 if(!EngineOutputIsUp()) continue;
\r
869 if(!MoveHistoryIsUp()) continue;
\r
871 // case 6: // input to eval graph does not seem to get here!
\r
872 // if(!EvalGraphIsUp()) continue;
\r
873 // h = evalGraphDialog; break;
\r
875 if(!appData.icsActive) continue;
\r
879 if(!appData.icsActive) continue;
\r
885 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
886 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
889 continue; // this message now has been processed
\r
893 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
894 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
895 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
896 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
897 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
898 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
899 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
900 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
901 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
902 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
903 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
904 for(i=0; i<MAX_CHAT; i++)
\r
905 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
908 if(done) continue; // [HGM] chat: end patch
\r
909 TranslateMessage(&msg); /* Translates virtual key codes */
\r
910 DispatchMessage(&msg); /* Dispatches message to window */
\r
915 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
918 /*---------------------------------------------------------------------------*\
\r
920 * Initialization functions
\r
922 \*---------------------------------------------------------------------------*/
\r
926 { // update user logo if necessary
\r
927 static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;
\r
929 if(appData.autoLogo) {
\r
930 curName = UserName();
\r
931 if(strcmp(curName, oldUserName)) {
\r
932 GetCurrentDirectory(MSG_SIZ, dir);
\r
933 SetCurrentDirectory(installDir);
\r
934 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
935 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
936 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
937 if(userLogo == NULL)
\r
938 userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
939 SetCurrentDirectory(dir); /* return to prev directory */
\r
945 InitApplication(HINSTANCE hInstance)
\r
949 /* Fill in window class structure with parameters that describe the */
\r
952 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
953 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
954 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
955 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
956 wc.hInstance = hInstance; /* Owner of this class */
\r
957 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
958 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
959 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
960 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
961 wc.lpszClassName = szAppName; /* Name to register as */
\r
963 /* Register the window class and return success/failure code. */
\r
964 if (!RegisterClass(&wc)) return FALSE;
\r
966 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
967 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
969 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
970 wc.hInstance = hInstance;
\r
971 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
972 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
973 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
974 wc.lpszMenuName = NULL;
\r
975 wc.lpszClassName = szConsoleName;
\r
977 if (!RegisterClass(&wc)) return FALSE;
\r
982 /* Set by InitInstance, used by EnsureOnScreen */
\r
983 int screenHeight, screenWidth;
\r
986 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
988 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
989 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
990 if (*x > screenWidth - 32) *x = 0;
\r
991 if (*y > screenHeight - 32) *y = 0;
\r
992 if (*x < minX) *x = minX;
\r
993 if (*y < minY) *y = minY;
\r
997 LoadLogo(ChessProgramState *cps, int n, Boolean ics)
\r
999 char buf[MSG_SIZ], dir[MSG_SIZ];
\r
1000 GetCurrentDirectory(MSG_SIZ, dir);
\r
1001 SetCurrentDirectory(installDir);
\r
1002 if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {
\r
1003 cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1005 if (cps->programLogo == NULL && appData.debugMode) {
\r
1006 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );
\r
1008 } else if(appData.autoLogo) {
\r
1009 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
\r
1010 char *opponent = "";
\r
1011 if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;
\r
1012 if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;
\r
1013 sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);
\r
1014 if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {
\r
1015 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
1016 cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1019 if(appData.directory[n] && appData.directory[n][0]) {
\r
1020 SetCurrentDirectory(appData.directory[n]);
\r
1021 cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1024 SetCurrentDirectory(dir); /* return to prev directory */
\r
1030 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
1031 backTextureSquareSize = 0; // kludge to force recalculation of texturemode
\r
1033 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1034 if(liteBackTexture) DeleteObject(liteBackTexture);
\r
1035 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1036 liteBackTextureMode = appData.liteBackTextureMode;
\r
1038 if (liteBackTexture == NULL && appData.debugMode) {
\r
1039 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1043 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1044 if(darkBackTexture) DeleteObject(darkBackTexture);
\r
1045 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1046 darkBackTextureMode = appData.darkBackTextureMode;
\r
1048 if (darkBackTexture == NULL && appData.debugMode) {
\r
1049 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1055 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
1057 HWND hwnd; /* Main window handle. */
\r
1059 WINDOWPLACEMENT wp;
\r
1062 hInst = hInstance; /* Store instance handle in our global variable */
\r
1063 programName = szAppName;
\r
1065 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
1066 *filepart = NULLCHAR;
\r
1067 SetCurrentDirectory(installDir);
\r
1069 GetCurrentDirectory(MSG_SIZ, installDir);
\r
1071 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
1072 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
1073 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
1074 /* xboard, and older WinBoards, controlled the move sound with the
\r
1075 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
1076 always turn the option on (so that the backend will call us),
\r
1077 then let the user turn the sound off by setting it to silence if
\r
1078 desired. To accommodate old winboard.ini files saved by old
\r
1079 versions of WinBoard, we also turn off the sound if the option
\r
1080 was initially set to false. [HGM] taken out of InitAppData */
\r
1081 if (!appData.ringBellAfterMoves) {
\r
1082 sounds[(int)SoundMove].name = strdup("");
\r
1083 appData.ringBellAfterMoves = TRUE;
\r
1085 if (appData.debugMode) {
\r
1086 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
1087 setbuf(debugFP, NULL);
\r
1090 LoadLanguageFile(appData.language);
\r
1094 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
1095 // InitEngineUCI( installDir, &second );
\r
1097 /* Create a main window for this application instance. */
\r
1098 hwnd = CreateWindow(szAppName, szTitle,
\r
1099 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
1100 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
1101 NULL, NULL, hInstance, NULL);
\r
1104 /* If window could not be created, return "failure" */
\r
1109 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1110 LoadLogo(&first, 0, FALSE);
\r
1111 LoadLogo(&second, 1, appData.icsActive);
\r
1115 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1116 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1117 iconCurrent = iconWhite;
\r
1118 InitDrawingColors();
\r
1119 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1120 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1121 InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args
\r
1122 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1123 /* Compute window size for each board size, and use the largest
\r
1124 size that fits on this screen as the default. */
\r
1125 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1126 if (boardSize == (BoardSize)-1 &&
\r
1127 winH <= screenHeight
\r
1128 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1129 && winW <= screenWidth) {
\r
1130 boardSize = (BoardSize)ibs;
\r
1134 InitDrawingSizes(boardSize, 0);
\r
1135 RecentEngineMenu(appData.recentEngineList);
\r
1137 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1139 /* [AS] Load textures if specified */
\r
1142 mysrandom( (unsigned) time(NULL) );
\r
1144 /* [AS] Restore layout */
\r
1145 if( wpMoveHistory.visible ) {
\r
1146 MoveHistoryPopUp();
\r
1149 if( wpEvalGraph.visible ) {
\r
1153 if( wpEngineOutput.visible ) {
\r
1154 EngineOutputPopUp();
\r
1157 /* Make the window visible; update its client area; and return "success" */
\r
1158 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1159 wp.length = sizeof(WINDOWPLACEMENT);
\r
1161 wp.showCmd = nCmdShow;
\r
1162 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1163 wp.rcNormalPosition.left = wpMain.x;
\r
1164 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1165 wp.rcNormalPosition.top = wpMain.y;
\r
1166 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1167 SetWindowPlacement(hwndMain, &wp);
\r
1169 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1171 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1172 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1174 if (hwndConsole) {
\r
1176 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1177 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1179 ShowWindow(hwndConsole, nCmdShow);
\r
1180 SetActiveWindow(hwndConsole);
\r
1182 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1183 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1192 HMENU hmenu = GetMenu(hwndMain);
\r
1194 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1195 MF_BYCOMMAND|((appData.icsActive &&
\r
1196 *appData.icsCommPort != NULLCHAR) ?
\r
1197 MF_ENABLED : MF_GRAYED));
\r
1198 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1199 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1200 MF_CHECKED : MF_UNCHECKED));
\r
1203 //---------------------------------------------------------------------------------------------------------
\r
1205 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1206 #define XBOARD FALSE
\r
1208 #define OPTCHAR "/"
\r
1209 #define SEPCHAR "="
\r
1210 #define TOPLEVEL 0
\r
1214 // front-end part of option handling
\r
1217 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1219 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1220 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1223 lf->lfEscapement = 0;
\r
1224 lf->lfOrientation = 0;
\r
1225 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1226 lf->lfItalic = mfp->italic;
\r
1227 lf->lfUnderline = mfp->underline;
\r
1228 lf->lfStrikeOut = mfp->strikeout;
\r
1229 lf->lfCharSet = mfp->charset;
\r
1230 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1231 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1232 lf->lfQuality = DEFAULT_QUALITY;
\r
1233 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1234 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1238 CreateFontInMF(MyFont *mf)
\r
1240 LFfromMFP(&mf->lf, &mf->mfp);
\r
1241 if (mf->hf) DeleteObject(mf->hf);
\r
1242 mf->hf = CreateFontIndirect(&mf->lf);
\r
1245 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1247 colorVariable[] = {
\r
1248 &whitePieceColor,
\r
1249 &blackPieceColor,
\r
1250 &lightSquareColor,
\r
1251 &darkSquareColor,
\r
1252 &highlightSquareColor,
\r
1253 &premoveHighlightColor,
\r
1255 &consoleBackgroundColor,
\r
1256 &appData.fontForeColorWhite,
\r
1257 &appData.fontBackColorWhite,
\r
1258 &appData.fontForeColorBlack,
\r
1259 &appData.fontBackColorBlack,
\r
1260 &appData.evalHistColorWhite,
\r
1261 &appData.evalHistColorBlack,
\r
1262 &appData.highlightArrowColor,
\r
1265 /* Command line font name parser. NULL name means do nothing.
\r
1266 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1267 For backward compatibility, syntax without the colon is also
\r
1268 accepted, but font names with digits in them won't work in that case.
\r
1271 ParseFontName(char *name, MyFontParams *mfp)
\r
1274 if (name == NULL) return;
\r
1276 q = strchr(p, ':');
\r
1278 if (q - p >= sizeof(mfp->faceName))
\r
1279 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1280 memcpy(mfp->faceName, p, q - p);
\r
1281 mfp->faceName[q - p] = NULLCHAR;
\r
1284 q = mfp->faceName;
\r
1286 while (*p && !isdigit(*p)) {
\r
1288 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1289 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1291 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1294 if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);
\r
1295 mfp->pointSize = (float) atof(p);
\r
1296 mfp->bold = (strchr(p, 'b') != NULL);
\r
1297 mfp->italic = (strchr(p, 'i') != NULL);
\r
1298 mfp->underline = (strchr(p, 'u') != NULL);
\r
1299 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1300 mfp->charset = DEFAULT_CHARSET;
\r
1301 q = strchr(p, 'c');
\r
1303 mfp->charset = (BYTE) atoi(q+1);
\r
1307 ParseFont(char *name, int number)
\r
1308 { // wrapper to shield back-end from 'font'
\r
1309 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1314 { // in WB we have a 2D array of fonts; this initializes their description
\r
1316 /* Point font array elements to structures and
\r
1317 parse default font names */
\r
1318 for (i=0; i<NUM_FONTS; i++) {
\r
1319 for (j=0; j<NUM_SIZES; j++) {
\r
1320 font[j][i] = &fontRec[j][i];
\r
1321 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1328 { // here we create the actual fonts from the selected descriptions
\r
1330 for (i=0; i<NUM_FONTS; i++) {
\r
1331 for (j=0; j<NUM_SIZES; j++) {
\r
1332 CreateFontInMF(font[j][i]);
\r
1336 /* Color name parser.
\r
1337 X version accepts X color names, but this one
\r
1338 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1340 ParseColorName(char *name)
\r
1342 int red, green, blue, count;
\r
1343 char buf[MSG_SIZ];
\r
1345 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1347 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1348 &red, &green, &blue);
\r
1351 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1352 DisplayError(buf, 0);
\r
1353 return RGB(0, 0, 0);
\r
1355 return PALETTERGB(red, green, blue);
\r
1359 ParseColor(int n, char *name)
\r
1360 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1361 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1365 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1367 char *e = argValue;
\r
1371 if (*e == 'b') eff |= CFE_BOLD;
\r
1372 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1373 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1374 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1375 else if (*e == '#' || isdigit(*e)) break;
\r
1379 *color = ParseColorName(e);
\r
1383 ParseTextAttribs(ColorClass cc, char *s)
\r
1384 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1385 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1386 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1390 ParseBoardSize(void *addr, char *name)
\r
1391 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1392 BoardSize bs = SizeTiny;
\r
1393 while (sizeInfo[bs].name != NULL) {
\r
1394 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1395 *(BoardSize *)addr = bs;
\r
1400 ExitArgError(_("Unrecognized board size value"), name, TRUE);
\r
1405 { // [HGM] import name from appData first
\r
1408 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1409 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1410 textAttribs[cc].sound.data = NULL;
\r
1411 MyLoadSound(&textAttribs[cc].sound);
\r
1413 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1414 textAttribs[cc].sound.name = strdup("");
\r
1415 textAttribs[cc].sound.data = NULL;
\r
1417 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1418 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1419 sounds[sc].data = NULL;
\r
1420 MyLoadSound(&sounds[sc]);
\r
1425 SetCommPortDefaults()
\r
1427 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1428 dcb.DCBlength = sizeof(DCB);
\r
1429 dcb.BaudRate = 9600;
\r
1430 dcb.fBinary = TRUE;
\r
1431 dcb.fParity = FALSE;
\r
1432 dcb.fOutxCtsFlow = FALSE;
\r
1433 dcb.fOutxDsrFlow = FALSE;
\r
1434 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1435 dcb.fDsrSensitivity = FALSE;
\r
1436 dcb.fTXContinueOnXoff = TRUE;
\r
1437 dcb.fOutX = FALSE;
\r
1439 dcb.fNull = FALSE;
\r
1440 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1441 dcb.fAbortOnError = FALSE;
\r
1443 dcb.Parity = SPACEPARITY;
\r
1444 dcb.StopBits = ONESTOPBIT;
\r
1447 // [HGM] args: these three cases taken out to stay in front-end
\r
1449 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1450 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1451 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1452 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1454 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1455 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1456 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1457 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1458 ad->argName, mfp->faceName, mfp->pointSize,
\r
1459 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1460 mfp->bold ? "b" : "",
\r
1461 mfp->italic ? "i" : "",
\r
1462 mfp->underline ? "u" : "",
\r
1463 mfp->strikeout ? "s" : "",
\r
1464 (int)mfp->charset);
\r
1470 { // [HGM] copy the names from the internal WB variables to appData
\r
1473 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1474 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1475 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1476 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1480 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1481 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1482 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1483 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1484 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1485 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1486 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1487 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1488 (ta->effects) ? " " : "",
\r
1489 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1493 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1494 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1495 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1496 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1497 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1501 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1502 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1503 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1507 ParseCommPortSettings(char *s)
\r
1508 { // wrapper to keep dcb from back-end
\r
1509 ParseCommSettings(s, &dcb);
\r
1514 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1515 GetActualPlacement(hwndMain, &wpMain);
\r
1516 GetActualPlacement(hwndConsole, &wpConsole);
\r
1517 GetActualPlacement(commentDialog, &wpComment);
\r
1518 GetActualPlacement(editTagsDialog, &wpTags);
\r
1519 GetActualPlacement(gameListDialog, &wpGameList);
\r
1520 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1521 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1522 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1526 PrintCommPortSettings(FILE *f, char *name)
\r
1527 { // wrapper to shield back-end from DCB
\r
1528 PrintCommSettings(f, name, &dcb);
\r
1532 MySearchPath(char *installDir, char *name, char *fullname)
\r
1534 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1535 if(name[0]== '%') {
\r
1536 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1537 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1538 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1539 *strchr(buf, '%') = 0;
\r
1540 strcat(fullname, getenv(buf));
\r
1541 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1543 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1544 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1545 return (int) strlen(fullname);
\r
1547 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1551 MyGetFullPathName(char *name, char *fullname)
\r
1554 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1559 { // [HGM] args: allows testing if main window is realized from back-end
\r
1560 return hwndMain != NULL;
\r
1564 PopUpStartupDialog()
\r
1568 LoadLanguageFile(appData.language);
\r
1569 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1570 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1571 FreeProcInstance(lpProc);
\r
1574 /*---------------------------------------------------------------------------*\
\r
1576 * GDI board drawing routines
\r
1578 \*---------------------------------------------------------------------------*/
\r
1580 /* [AS] Draw square using background texture */
\r
1581 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1586 return; /* Should never happen! */
\r
1589 SetGraphicsMode( dst, GM_ADVANCED );
\r
1596 /* X reflection */
\r
1601 x.eDx = (FLOAT) dw + dx - 1;
\r
1604 SetWorldTransform( dst, &x );
\r
1607 /* Y reflection */
\r
1613 x.eDy = (FLOAT) dh + dy - 1;
\r
1615 SetWorldTransform( dst, &x );
\r
1623 x.eDx = (FLOAT) dx;
\r
1624 x.eDy = (FLOAT) dy;
\r
1627 SetWorldTransform( dst, &x );
\r
1631 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1639 SetWorldTransform( dst, &x );
\r
1641 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1644 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1646 PM_WP = (int) WhitePawn,
\r
1647 PM_WN = (int) WhiteKnight,
\r
1648 PM_WB = (int) WhiteBishop,
\r
1649 PM_WR = (int) WhiteRook,
\r
1650 PM_WQ = (int) WhiteQueen,
\r
1651 PM_WF = (int) WhiteFerz,
\r
1652 PM_WW = (int) WhiteWazir,
\r
1653 PM_WE = (int) WhiteAlfil,
\r
1654 PM_WM = (int) WhiteMan,
\r
1655 PM_WO = (int) WhiteCannon,
\r
1656 PM_WU = (int) WhiteUnicorn,
\r
1657 PM_WH = (int) WhiteNightrider,
\r
1658 PM_WA = (int) WhiteAngel,
\r
1659 PM_WC = (int) WhiteMarshall,
\r
1660 PM_WAB = (int) WhiteCardinal,
\r
1661 PM_WD = (int) WhiteDragon,
\r
1662 PM_WL = (int) WhiteLance,
\r
1663 PM_WS = (int) WhiteCobra,
\r
1664 PM_WV = (int) WhiteFalcon,
\r
1665 PM_WSG = (int) WhiteSilver,
\r
1666 PM_WG = (int) WhiteGrasshopper,
\r
1667 PM_WK = (int) WhiteKing,
\r
1668 PM_BP = (int) BlackPawn,
\r
1669 PM_BN = (int) BlackKnight,
\r
1670 PM_BB = (int) BlackBishop,
\r
1671 PM_BR = (int) BlackRook,
\r
1672 PM_BQ = (int) BlackQueen,
\r
1673 PM_BF = (int) BlackFerz,
\r
1674 PM_BW = (int) BlackWazir,
\r
1675 PM_BE = (int) BlackAlfil,
\r
1676 PM_BM = (int) BlackMan,
\r
1677 PM_BO = (int) BlackCannon,
\r
1678 PM_BU = (int) BlackUnicorn,
\r
1679 PM_BH = (int) BlackNightrider,
\r
1680 PM_BA = (int) BlackAngel,
\r
1681 PM_BC = (int) BlackMarshall,
\r
1682 PM_BG = (int) BlackGrasshopper,
\r
1683 PM_BAB = (int) BlackCardinal,
\r
1684 PM_BD = (int) BlackDragon,
\r
1685 PM_BL = (int) BlackLance,
\r
1686 PM_BS = (int) BlackCobra,
\r
1687 PM_BV = (int) BlackFalcon,
\r
1688 PM_BSG = (int) BlackSilver,
\r
1689 PM_BK = (int) BlackKing
\r
1692 static HFONT hPieceFont = NULL;
\r
1693 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1694 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1695 static int fontBitmapSquareSize = 0;
\r
1696 static char pieceToFontChar[(int) EmptySquare] =
\r
1697 { 'p', 'n', 'b', 'r', 'q',
\r
1698 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1699 'k', 'o', 'm', 'v', 't', 'w',
\r
1700 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1703 extern BOOL SetCharTable( char *table, const char * map );
\r
1704 /* [HGM] moved to backend.c */
\r
1706 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1709 BYTE r1 = GetRValue( color );
\r
1710 BYTE g1 = GetGValue( color );
\r
1711 BYTE b1 = GetBValue( color );
\r
1717 /* Create a uniform background first */
\r
1718 hbrush = CreateSolidBrush( color );
\r
1719 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1720 FillRect( hdc, &rc, hbrush );
\r
1721 DeleteObject( hbrush );
\r
1724 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1725 int steps = squareSize / 2;
\r
1728 for( i=0; i<steps; i++ ) {
\r
1729 BYTE r = r1 - (r1-r2) * i / steps;
\r
1730 BYTE g = g1 - (g1-g2) * i / steps;
\r
1731 BYTE b = b1 - (b1-b2) * i / steps;
\r
1733 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1734 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1735 FillRect( hdc, &rc, hbrush );
\r
1736 DeleteObject(hbrush);
\r
1739 else if( mode == 2 ) {
\r
1740 /* Diagonal gradient, good more or less for every piece */
\r
1741 POINT triangle[3];
\r
1742 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1743 HBRUSH hbrush_old;
\r
1744 int steps = squareSize;
\r
1747 triangle[0].x = squareSize - steps;
\r
1748 triangle[0].y = squareSize;
\r
1749 triangle[1].x = squareSize;
\r
1750 triangle[1].y = squareSize;
\r
1751 triangle[2].x = squareSize;
\r
1752 triangle[2].y = squareSize - steps;
\r
1754 for( i=0; i<steps; i++ ) {
\r
1755 BYTE r = r1 - (r1-r2) * i / steps;
\r
1756 BYTE g = g1 - (g1-g2) * i / steps;
\r
1757 BYTE b = b1 - (b1-b2) * i / steps;
\r
1759 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1760 hbrush_old = SelectObject( hdc, hbrush );
\r
1761 Polygon( hdc, triangle, 3 );
\r
1762 SelectObject( hdc, hbrush_old );
\r
1763 DeleteObject(hbrush);
\r
1768 SelectObject( hdc, hpen );
\r
1773 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1774 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1775 piece: follow the steps as explained below.
\r
1777 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1781 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1785 int backColor = whitePieceColor;
\r
1786 int foreColor = blackPieceColor;
\r
1788 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1789 backColor = appData.fontBackColorWhite;
\r
1790 foreColor = appData.fontForeColorWhite;
\r
1792 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1793 backColor = appData.fontBackColorBlack;
\r
1794 foreColor = appData.fontForeColorBlack;
\r
1798 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1800 hbm_old = SelectObject( hdc, hbm );
\r
1804 rc.right = squareSize;
\r
1805 rc.bottom = squareSize;
\r
1807 /* Step 1: background is now black */
\r
1808 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1810 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1812 pt.x = (squareSize - sz.cx) / 2;
\r
1813 pt.y = (squareSize - sz.cy) / 2;
\r
1815 SetBkMode( hdc, TRANSPARENT );
\r
1816 SetTextColor( hdc, chroma );
\r
1817 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1818 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1820 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1821 /* Step 3: the area outside the piece is filled with white */
\r
1822 // FloodFill( hdc, 0, 0, chroma );
\r
1823 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1824 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1825 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1826 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1827 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1829 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1830 but if the start point is not inside the piece we're lost!
\r
1831 There should be a better way to do this... if we could create a region or path
\r
1832 from the fill operation we would be fine for example.
\r
1834 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1835 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1837 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1838 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1839 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1841 SelectObject( dc2, bm2 );
\r
1842 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1843 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1844 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1845 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1846 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1849 DeleteObject( bm2 );
\r
1852 SetTextColor( hdc, 0 );
\r
1854 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1855 draw the piece again in black for safety.
\r
1857 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1859 SelectObject( hdc, hbm_old );
\r
1861 if( hPieceMask[index] != NULL ) {
\r
1862 DeleteObject( hPieceMask[index] );
\r
1865 hPieceMask[index] = hbm;
\r
1868 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1870 SelectObject( hdc, hbm );
\r
1873 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1874 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1875 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1877 SelectObject( dc1, hPieceMask[index] );
\r
1878 SelectObject( dc2, bm2 );
\r
1879 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1880 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1883 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1884 the piece background and deletes (makes transparent) the rest.
\r
1885 Thanks to that mask, we are free to paint the background with the greates
\r
1886 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1887 We use this, to make gradients and give the pieces a "roundish" look.
\r
1889 SetPieceBackground( hdc, backColor, 2 );
\r
1890 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1894 DeleteObject( bm2 );
\r
1897 SetTextColor( hdc, foreColor );
\r
1898 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1900 SelectObject( hdc, hbm_old );
\r
1902 if( hPieceFace[index] != NULL ) {
\r
1903 DeleteObject( hPieceFace[index] );
\r
1906 hPieceFace[index] = hbm;
\r
1909 static int TranslatePieceToFontPiece( int piece )
\r
1939 case BlackMarshall:
\r
1943 case BlackNightrider:
\r
1949 case BlackUnicorn:
\r
1953 case BlackGrasshopper:
\r
1965 case BlackCardinal:
\r
1972 case WhiteMarshall:
\r
1976 case WhiteNightrider:
\r
1982 case WhiteUnicorn:
\r
1986 case WhiteGrasshopper:
\r
1998 case WhiteCardinal:
\r
2007 void CreatePiecesFromFont()
\r
2010 HDC hdc_window = NULL;
\r
2016 if( fontBitmapSquareSize < 0 ) {
\r
2017 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
2021 if( !appData.useFont || appData.renderPiecesWithFont == NULL ||
\r
2022 appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
2023 fontBitmapSquareSize = -1;
\r
2027 if( fontBitmapSquareSize != squareSize ) {
\r
2028 hdc_window = GetDC( hwndMain );
\r
2029 hdc = CreateCompatibleDC( hdc_window );
\r
2031 if( hPieceFont != NULL ) {
\r
2032 DeleteObject( hPieceFont );
\r
2035 for( i=0; i<=(int)BlackKing; i++ ) {
\r
2036 hPieceMask[i] = NULL;
\r
2037 hPieceFace[i] = NULL;
\r
2043 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
2044 fontHeight = appData.fontPieceSize;
\r
2047 fontHeight = (fontHeight * squareSize) / 100;
\r
2049 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2051 lf.lfEscapement = 0;
\r
2052 lf.lfOrientation = 0;
\r
2053 lf.lfWeight = FW_NORMAL;
\r
2055 lf.lfUnderline = 0;
\r
2056 lf.lfStrikeOut = 0;
\r
2057 lf.lfCharSet = DEFAULT_CHARSET;
\r
2058 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2059 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2060 lf.lfQuality = PROOF_QUALITY;
\r
2061 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2062 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2063 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2065 hPieceFont = CreateFontIndirect( &lf );
\r
2067 if( hPieceFont == NULL ) {
\r
2068 fontBitmapSquareSize = -2;
\r
2071 /* Setup font-to-piece character table */
\r
2072 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2073 /* No (or wrong) global settings, try to detect the font */
\r
2074 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2076 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2078 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2079 /* DiagramTT* family */
\r
2080 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2082 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2083 /* Fairy symbols */
\r
2084 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2086 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2087 /* Good Companion (Some characters get warped as literal :-( */
\r
2088 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2089 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2090 SetCharTable(pieceToFontChar, s);
\r
2093 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2094 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2098 /* Create bitmaps */
\r
2099 hfont_old = SelectObject( hdc, hPieceFont );
\r
2100 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2101 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2102 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2104 SelectObject( hdc, hfont_old );
\r
2106 fontBitmapSquareSize = squareSize;
\r
2110 if( hdc != NULL ) {
\r
2114 if( hdc_window != NULL ) {
\r
2115 ReleaseDC( hwndMain, hdc_window );
\r
2120 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2122 char name[128], buf[MSG_SIZ];
\r
2124 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2125 if(appData.pieceDirectory[0]) {
\r
2127 snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);
\r
2128 res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
2129 if(res) return res;
\r
2131 if (gameInfo.event &&
\r
2132 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2133 strcmp(name, "k80s") == 0) {
\r
2134 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2136 return LoadBitmap(hinst, name);
\r
2140 /* Insert a color into the program's logical palette
\r
2141 structure. This code assumes the given color is
\r
2142 the result of the RGB or PALETTERGB macro, and it
\r
2143 knows how those macros work (which is documented).
\r
2146 InsertInPalette(COLORREF color)
\r
2148 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2150 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2151 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2152 pLogPal->palNumEntries--;
\r
2156 pe->peFlags = (char) 0;
\r
2157 pe->peRed = (char) (0xFF & color);
\r
2158 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2159 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2165 InitDrawingColors()
\r
2168 if (pLogPal == NULL) {
\r
2169 /* Allocate enough memory for a logical palette with
\r
2170 * PALETTESIZE entries and set the size and version fields
\r
2171 * of the logical palette structure.
\r
2173 pLogPal = (NPLOGPALETTE)
\r
2174 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2175 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2176 pLogPal->palVersion = 0x300;
\r
2178 pLogPal->palNumEntries = 0;
\r
2180 InsertInPalette(lightSquareColor);
\r
2181 InsertInPalette(darkSquareColor);
\r
2182 InsertInPalette(whitePieceColor);
\r
2183 InsertInPalette(blackPieceColor);
\r
2184 InsertInPalette(highlightSquareColor);
\r
2185 InsertInPalette(premoveHighlightColor);
\r
2187 /* create a logical color palette according the information
\r
2188 * in the LOGPALETTE structure.
\r
2190 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2192 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2193 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2194 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2195 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2196 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2197 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2198 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2199 for(i=0; i<8;i++) markerBrush[i] = CreateSolidBrush(markerColor[i]); // [HGM] markers
\r
2201 /* [AS] Force rendering of the font-based pieces */
\r
2202 if( fontBitmapSquareSize > 0 ) {
\r
2203 fontBitmapSquareSize = 0;
\r
2209 BoardWidth(int boardSize, int n)
\r
2210 { /* [HGM] argument n added to allow different width and height */
\r
2211 int lineGap = sizeInfo[boardSize].lineGap;
\r
2213 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2214 lineGap = appData.overrideLineGap;
\r
2217 return (n + 1) * lineGap +
\r
2218 n * sizeInfo[boardSize].squareSize;
\r
2221 /* Respond to board resize by dragging edge */
\r
2223 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2225 BoardSize newSize = NUM_SIZES - 1;
\r
2226 static int recurse = 0;
\r
2227 if (IsIconic(hwndMain)) return;
\r
2228 if (recurse > 0) return;
\r
2230 while (newSize > 0) {
\r
2231 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2232 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2233 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2236 boardSize = newSize;
\r
2237 InitDrawingSizes(boardSize, flags);
\r
2242 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2245 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2247 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2248 ChessSquare piece;
\r
2249 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2251 SIZE clockSize, messageSize;
\r
2253 char buf[MSG_SIZ];
\r
2255 HMENU hmenu = GetMenu(hwndMain);
\r
2256 RECT crect, wrect, oldRect;
\r
2258 LOGBRUSH logbrush;
\r
2259 VariantClass v = gameInfo.variant;
\r
2261 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2262 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2264 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2265 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2266 if(boardSize == -1) return; // no size defined yet; abort (to allow early call of InitPosition)
\r
2267 oldBoardSize = boardSize;
\r
2269 if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)
\r
2270 { // correct board size to one where built-in pieces exist
\r
2271 if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)
\r
2272 && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range
\r
2273 || (v == VariantShogi && boardSize != SizeModerate) // Japanese-style Shogi
\r
2274 || v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan
\r
2275 || v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy || v == VariantLion ) {
\r
2276 if(boardSize < SizeMediocre) boardSize = SizePetite; else
\r
2277 if(boardSize > SizeModerate) boardSize = SizeBulky; else
\r
2278 boardSize = SizeMiddling;
\r
2281 if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite
\r
2283 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2284 oldRect.top = wpMain.y;
\r
2285 oldRect.right = wpMain.x + wpMain.width;
\r
2286 oldRect.bottom = wpMain.y + wpMain.height;
\r
2288 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2289 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2290 squareSize = sizeInfo[boardSize].squareSize;
\r
2291 lineGap = sizeInfo[boardSize].lineGap;
\r
2292 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2293 border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;
\r
2295 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2296 lineGap = appData.overrideLineGap;
\r
2299 if (tinyLayout != oldTinyLayout) {
\r
2300 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2302 style &= ~WS_SYSMENU;
\r
2303 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2304 "&Minimize\tCtrl+F4");
\r
2306 style |= WS_SYSMENU;
\r
2307 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2309 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2311 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2312 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2313 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2315 DrawMenuBar(hwndMain);
\r
2318 boardWidth = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;
\r
2319 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;
\r
2321 /* Get text area sizes */
\r
2322 hdc = GetDC(hwndMain);
\r
2323 if (appData.clockMode) {
\r
2324 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2326 snprintf(buf, MSG_SIZ, _("White"));
\r
2328 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2329 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2330 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2331 str = _("We only care about the height here");
\r
2332 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2333 SelectObject(hdc, oldFont);
\r
2334 ReleaseDC(hwndMain, hdc);
\r
2336 /* Compute where everything goes */
\r
2337 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2338 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2339 logoHeight = 2*clockSize.cy;
\r
2340 leftLogoRect.left = OUTER_MARGIN;
\r
2341 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2342 leftLogoRect.top = OUTER_MARGIN;
\r
2343 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2345 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2346 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2347 rightLogoRect.top = OUTER_MARGIN;
\r
2348 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2351 whiteRect.left = leftLogoRect.right;
\r
2352 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2353 whiteRect.top = OUTER_MARGIN;
\r
2354 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2356 blackRect.right = rightLogoRect.left;
\r
2357 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2358 blackRect.top = whiteRect.top;
\r
2359 blackRect.bottom = whiteRect.bottom;
\r
2361 whiteRect.left = OUTER_MARGIN;
\r
2362 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2363 whiteRect.top = OUTER_MARGIN;
\r
2364 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2366 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2367 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2368 blackRect.top = whiteRect.top;
\r
2369 blackRect.bottom = whiteRect.bottom;
\r
2371 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2374 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2375 if (appData.showButtonBar) {
\r
2376 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2377 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2379 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2381 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2382 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2384 boardRect.left = OUTER_MARGIN;
\r
2385 boardRect.right = boardRect.left + boardWidth;
\r
2386 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2387 boardRect.bottom = boardRect.top + boardHeight;
\r
2389 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2390 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2391 oldTinyLayout = tinyLayout;
\r
2392 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2393 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2394 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2395 winW *= 1 + twoBoards;
\r
2396 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2397 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2398 wpMain.height = winH; // without disturbing window attachments
\r
2399 GetWindowRect(hwndMain, &wrect);
\r
2400 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2401 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2403 // [HGM] placement: let attached windows follow size change.
\r
2404 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2405 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2406 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2407 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2408 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2410 /* compensate if menu bar wrapped */
\r
2411 GetClientRect(hwndMain, &crect);
\r
2412 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2413 wpMain.height += offby;
\r
2415 case WMSZ_TOPLEFT:
\r
2416 SetWindowPos(hwndMain, NULL,
\r
2417 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2418 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2421 case WMSZ_TOPRIGHT:
\r
2423 SetWindowPos(hwndMain, NULL,
\r
2424 wrect.left, wrect.bottom - wpMain.height,
\r
2425 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2428 case WMSZ_BOTTOMLEFT:
\r
2430 SetWindowPos(hwndMain, NULL,
\r
2431 wrect.right - wpMain.width, wrect.top,
\r
2432 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2435 case WMSZ_BOTTOMRIGHT:
\r
2439 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2440 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2445 for (i = 0; i < N_BUTTONS; i++) {
\r
2446 if (buttonDesc[i].hwnd != NULL) {
\r
2447 DestroyWindow(buttonDesc[i].hwnd);
\r
2448 buttonDesc[i].hwnd = NULL;
\r
2450 if (appData.showButtonBar) {
\r
2451 buttonDesc[i].hwnd =
\r
2452 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2453 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2454 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2455 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2456 (HMENU) buttonDesc[i].id,
\r
2457 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2459 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2460 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2461 MAKELPARAM(FALSE, 0));
\r
2463 if (buttonDesc[i].id == IDM_Pause)
\r
2464 hwndPause = buttonDesc[i].hwnd;
\r
2465 buttonDesc[i].wndproc = (WNDPROC)
\r
2466 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2469 if (gridPen != NULL) DeleteObject(gridPen);
\r
2470 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2471 if (premovePen != NULL) DeleteObject(premovePen);
\r
2472 if (lineGap != 0) {
\r
2473 logbrush.lbStyle = BS_SOLID;
\r
2474 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2476 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2477 lineGap, &logbrush, 0, NULL);
\r
2478 logbrush.lbColor = highlightSquareColor;
\r
2480 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2481 lineGap, &logbrush, 0, NULL);
\r
2483 logbrush.lbColor = premoveHighlightColor;
\r
2485 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2486 lineGap, &logbrush, 0, NULL);
\r
2488 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2489 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2490 gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;
\r
2491 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2492 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;
\r
2493 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2494 BOARD_WIDTH * (squareSize + lineGap) + border;
\r
2495 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2497 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2498 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;
\r
2499 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2500 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2501 lineGap / 2 + (i * (squareSize + lineGap)) + border;
\r
2502 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2503 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;
\r
2504 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2508 /* [HGM] Licensing requirement */
\r
2510 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2513 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2515 GothicPopUp( "", VariantNormal);
\r
2518 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2520 /* Load piece bitmaps for this board size */
\r
2521 for (i=0; i<=2; i++) {
\r
2522 for (piece = WhitePawn;
\r
2523 (int) piece < (int) BlackPawn;
\r
2524 piece = (ChessSquare) ((int) piece + 1)) {
\r
2525 if (pieceBitmap[i][piece] != NULL)
\r
2526 DeleteObject(pieceBitmap[i][piece]);
\r
2530 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2531 // Orthodox Chess pieces
\r
2532 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2533 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2534 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2535 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2536 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2537 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2538 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2539 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2540 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2541 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2542 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2543 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2544 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2545 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2546 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2547 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2548 // in Shogi, Hijack the unused Queen for Lance
\r
2549 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2550 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2551 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2553 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2554 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2555 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2558 if(squareSize <= 72 && squareSize >= 33) {
\r
2559 /* A & C are available in most sizes now */
\r
2560 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2561 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2562 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2563 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2564 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2565 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2566 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2567 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2568 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2569 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2570 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2571 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2572 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2573 } else { // Smirf-like
\r
2574 if(gameInfo.variant == VariantSChess) {
\r
2575 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2576 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2577 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2579 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2580 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2581 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2584 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2585 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2586 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2587 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2588 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2589 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2590 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2591 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2592 } else { // WinBoard standard
\r
2593 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2594 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2595 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2600 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2601 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2602 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2603 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2604 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2605 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2606 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2607 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2608 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2609 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2610 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2611 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2612 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2613 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2614 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2615 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2616 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2617 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2618 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2619 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2620 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2621 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2622 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2623 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2624 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2625 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2626 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2627 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2628 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2629 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2630 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2631 pieceBitmap[0][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "s");
\r
2632 pieceBitmap[1][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "o");
\r
2633 pieceBitmap[2][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "w");
\r
2635 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2636 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2637 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2638 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2639 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2640 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2641 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2642 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2643 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2644 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2645 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2646 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2647 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2649 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2650 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2651 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2652 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2653 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2654 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2655 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2656 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2657 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2658 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2659 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2660 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2663 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2664 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2665 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2666 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2667 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2668 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2669 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2670 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2671 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2672 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2673 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2674 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2675 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2676 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2677 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2681 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2682 /* special Shogi support in this size */
\r
2683 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2684 for (piece = WhitePawn;
\r
2685 (int) piece < (int) BlackPawn;
\r
2686 piece = (ChessSquare) ((int) piece + 1)) {
\r
2687 if (pieceBitmap[i][piece] != NULL)
\r
2688 DeleteObject(pieceBitmap[i][piece]);
\r
2691 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2692 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2693 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2694 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2695 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2696 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2697 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2698 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2699 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2700 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2701 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2702 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2703 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2704 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2705 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2706 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2707 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2708 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2709 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2710 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2711 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2712 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2713 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2714 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2715 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2716 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2717 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2718 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2719 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2720 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2721 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2722 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2723 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2724 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2725 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2726 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2727 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2728 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2729 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2730 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2731 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2732 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2738 PieceBitmap(ChessSquare p, int kind)
\r
2740 if ((int) p >= (int) BlackPawn)
\r
2741 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2743 return pieceBitmap[kind][(int) p];
\r
2746 /***************************************************************/
\r
2748 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2749 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2751 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2752 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2756 SquareToPos(int row, int column, int * x, int * y)
\r
2759 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
2760 *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;
\r
2762 *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;
\r
2763 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
2768 DrawCoordsOnDC(HDC hdc)
\r
2770 static char files[] = "0123456789012345678901221098765432109876543210";
\r
2771 static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";
\r
2772 char str[2] = { NULLCHAR, NULLCHAR };
\r
2773 int oldMode, oldAlign, x, y, start, i;
\r
2777 if (!appData.showCoords)
\r
2780 start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;
\r
2782 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2783 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2784 oldAlign = GetTextAlign(hdc);
\r
2785 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2787 y = boardRect.top + lineGap;
\r
2788 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2791 SetTextAlign(hdc, TA_RIGHT|TA_TOP);
\r
2792 x += border - lineGap - 4; y += squareSize - 6;
\r
2794 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2795 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2796 str[0] = files[start + i];
\r
2797 ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);
\r
2798 y += squareSize + lineGap;
\r
2801 start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;
\r
2804 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2805 x += -border + 4; y += border - squareSize + 6;
\r
2807 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2808 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2809 str[0] = ranks[start + i];
\r
2810 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2811 x += squareSize + lineGap;
\r
2814 SelectObject(hdc, oldBrush);
\r
2815 SetBkMode(hdc, oldMode);
\r
2816 SetTextAlign(hdc, oldAlign);
\r
2817 SelectObject(hdc, oldFont);
\r
2821 DrawGridOnDC(HDC hdc)
\r
2825 if (lineGap != 0) {
\r
2826 oldPen = SelectObject(hdc, gridPen);
\r
2827 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2828 SelectObject(hdc, oldPen);
\r
2832 #define HIGHLIGHT_PEN 0
\r
2833 #define PREMOVE_PEN 1
\r
2836 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2839 HPEN oldPen, hPen;
\r
2840 if (lineGap == 0) return;
\r
2842 x1 = boardRect.left +
\r
2843 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;
\r
2844 y1 = boardRect.top +
\r
2845 lineGap/2 + y * (squareSize + lineGap) + border;
\r
2847 x1 = boardRect.left +
\r
2848 lineGap/2 + x * (squareSize + lineGap) + border;
\r
2849 y1 = boardRect.top +
\r
2850 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;
\r
2852 hPen = pen ? premovePen : highlightPen;
\r
2853 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2854 MoveToEx(hdc, x1, y1, NULL);
\r
2855 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2856 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2857 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2858 LineTo(hdc, x1, y1);
\r
2859 SelectObject(hdc, oldPen);
\r
2863 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2866 for (i=0; i<2; i++) {
\r
2867 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2868 DrawHighlightOnDC(hdc, TRUE,
\r
2869 h->sq[i].x, h->sq[i].y,
\r
2874 /* Note: sqcolor is used only in monoMode */
\r
2875 /* Note that this code is largely duplicated in woptions.c,
\r
2876 function DrawSampleSquare, so that needs to be updated too */
\r
2878 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2880 HBITMAP oldBitmap;
\r
2884 if (appData.blindfold) return;
\r
2886 /* [AS] Use font-based pieces if needed */
\r
2887 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2888 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2889 CreatePiecesFromFont();
\r
2891 if( fontBitmapSquareSize == squareSize ) {
\r
2892 int index = TranslatePieceToFontPiece(piece);
\r
2894 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2896 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2897 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2901 squareSize, squareSize,
\r
2906 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2908 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2909 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2913 squareSize, squareSize,
\r
2922 if (appData.monoMode) {
\r
2923 SelectObject(tmphdc, PieceBitmap(piece,
\r
2924 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2925 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2926 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2928 HBRUSH xBrush = whitePieceBrush;
\r
2929 tmpSize = squareSize;
\r
2930 if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);
\r
2932 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2933 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2934 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2935 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2936 x += (squareSize - minorSize)>>1;
\r
2937 y += squareSize - minorSize - 2;
\r
2938 tmpSize = minorSize;
\r
2940 if (color || appData.allWhite ) {
\r
2941 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2943 oldBrush = SelectObject(hdc, xBrush);
\r
2944 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2945 if(appData.upsideDown && color==flipView)
\r
2946 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2948 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2949 /* Use black for outline of white pieces */
\r
2950 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2951 if(appData.upsideDown && color==flipView)
\r
2952 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2954 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2955 } else if(appData.pieceDirectory[0]) {
\r
2956 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2957 oldBrush = SelectObject(hdc, xBrush);
\r
2958 if(appData.upsideDown && color==flipView)
\r
2959 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2961 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2962 SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2963 if(appData.upsideDown && color==flipView)
\r
2964 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2966 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2968 /* Use square color for details of black pieces */
\r
2969 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2970 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2971 if(appData.upsideDown && !flipView)
\r
2972 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2974 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2976 SelectObject(hdc, oldBrush);
\r
2977 SelectObject(tmphdc, oldBitmap);
\r
2981 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2982 int GetBackTextureMode( int algo )
\r
2984 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2988 case BACK_TEXTURE_MODE_PLAIN:
\r
2989 result = 1; /* Always use identity map */
\r
2991 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2992 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
3000 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
3001 to handle redraws cleanly (as random numbers would always be different).
\r
3003 VOID RebuildTextureSquareInfo()
\r
3013 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
3015 if( liteBackTexture != NULL ) {
\r
3016 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3017 lite_w = bi.bmWidth;
\r
3018 lite_h = bi.bmHeight;
\r
3022 if( darkBackTexture != NULL ) {
\r
3023 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3024 dark_w = bi.bmWidth;
\r
3025 dark_h = bi.bmHeight;
\r
3029 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
3030 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
3031 if( (col + row) & 1 ) {
\r
3033 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
3034 if( lite_w >= squareSize*BOARD_WIDTH )
\r
3035 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
3037 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
3038 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
3039 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
3041 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
3042 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
3047 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
3048 if( dark_w >= squareSize*BOARD_WIDTH )
\r
3049 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
3051 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
3052 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
3053 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
3055 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
3056 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
3063 /* [AS] Arrow highlighting support */
\r
3065 static double A_WIDTH = 5; /* Width of arrow body */
\r
3067 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
3068 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
3070 static double Sqr( double x )
\r
3075 static int Round( double x )
\r
3077 return (int) (x + 0.5);
\r
3080 /* Draw an arrow between two points using current settings */
\r
3081 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
3084 double dx, dy, j, k, x, y;
\r
3086 if( d_x == s_x ) {
\r
3087 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3089 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
3092 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
3093 arrow[1].y = d_y - h;
\r
3095 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3096 arrow[2].y = d_y - h;
\r
3101 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3102 arrow[5].y = d_y - h;
\r
3104 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3105 arrow[4].y = d_y - h;
\r
3107 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3110 else if( d_y == s_y ) {
\r
3111 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3114 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3116 arrow[1].x = d_x - w;
\r
3117 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3119 arrow[2].x = d_x - w;
\r
3120 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3125 arrow[5].x = d_x - w;
\r
3126 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3128 arrow[4].x = d_x - w;
\r
3129 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3132 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3135 /* [AS] Needed a lot of paper for this! :-) */
\r
3136 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3137 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3139 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3141 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3146 arrow[0].x = Round(x - j);
\r
3147 arrow[0].y = Round(y + j*dx);
\r
3149 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3150 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3153 x = (double) d_x - k;
\r
3154 y = (double) d_y - k*dy;
\r
3157 x = (double) d_x + k;
\r
3158 y = (double) d_y + k*dy;
\r
3161 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3163 arrow[6].x = Round(x - j);
\r
3164 arrow[6].y = Round(y + j*dx);
\r
3166 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3167 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3169 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3170 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3175 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3176 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3179 Polygon( hdc, arrow, 7 );
\r
3182 /* [AS] Draw an arrow between two squares */
\r
3183 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3185 int s_x, s_y, d_x, d_y;
\r
3192 if( s_col == d_col && s_row == d_row ) {
\r
3196 /* Get source and destination points */
\r
3197 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3198 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3201 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3203 else if( d_y < s_y ) {
\r
3204 d_y += squareSize / 2 + squareSize / 4;
\r
3207 d_y += squareSize / 2;
\r
3211 d_x += squareSize / 2 - squareSize / 4;
\r
3213 else if( d_x < s_x ) {
\r
3214 d_x += squareSize / 2 + squareSize / 4;
\r
3217 d_x += squareSize / 2;
\r
3220 s_x += squareSize / 2;
\r
3221 s_y += squareSize / 2;
\r
3223 /* Adjust width */
\r
3224 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3227 stLB.lbStyle = BS_SOLID;
\r
3228 stLB.lbColor = appData.highlightArrowColor;
\r
3231 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3232 holdpen = SelectObject( hdc, hpen );
\r
3233 hbrush = CreateBrushIndirect( &stLB );
\r
3234 holdbrush = SelectObject( hdc, hbrush );
\r
3236 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3238 SelectObject( hdc, holdpen );
\r
3239 SelectObject( hdc, holdbrush );
\r
3240 DeleteObject( hpen );
\r
3241 DeleteObject( hbrush );
\r
3244 BOOL HasHighlightInfo()
\r
3246 BOOL result = FALSE;
\r
3248 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3249 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3260 BOOL IsDrawArrowEnabled()
\r
3262 BOOL result = FALSE;
\r
3264 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3271 VOID DrawArrowHighlight( HDC hdc )
\r
3273 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3274 DrawArrowBetweenSquares( hdc,
\r
3275 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3276 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3280 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3282 HRGN result = NULL;
\r
3284 if( HasHighlightInfo() ) {
\r
3285 int x1, y1, x2, y2;
\r
3286 int sx, sy, dx, dy;
\r
3288 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3289 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3291 sx = MIN( x1, x2 );
\r
3292 sy = MIN( y1, y2 );
\r
3293 dx = MAX( x1, x2 ) + squareSize;
\r
3294 dy = MAX( y1, y2 ) + squareSize;
\r
3296 result = CreateRectRgn( sx, sy, dx, dy );
\r
3303 Warning: this function modifies the behavior of several other functions.
\r
3305 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3306 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3307 repaint is scattered all over the place, which is not good for features such as
\r
3308 "arrow highlighting" that require a full repaint of the board.
\r
3310 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3311 user interaction, when speed is not so important) but especially to avoid errors
\r
3312 in the displayed graphics.
\r
3314 In such patched places, I always try refer to this function so there is a single
\r
3315 place to maintain knowledge.
\r
3317 To restore the original behavior, just return FALSE unconditionally.
\r
3319 BOOL IsFullRepaintPreferrable()
\r
3321 BOOL result = FALSE;
\r
3323 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3324 /* Arrow may appear on the board */
\r
3332 This function is called by DrawPosition to know whether a full repaint must
\r
3335 Only DrawPosition may directly call this function, which makes use of
\r
3336 some state information. Other function should call DrawPosition specifying
\r
3337 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3339 BOOL DrawPositionNeedsFullRepaint()
\r
3341 BOOL result = FALSE;
\r
3344 Probably a slightly better policy would be to trigger a full repaint
\r
3345 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3346 but animation is fast enough that it's difficult to notice.
\r
3348 if( animInfo.piece == EmptySquare ) {
\r
3349 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3357 static HBITMAP borderBitmap;
\r
3360 DrawBackgroundOnDC(HDC hdc)
\r
3366 static char oldBorder[MSG_SIZ];
\r
3367 int w = 600, h = 600, mode;
\r
3369 if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid
\r
3370 strncpy(oldBorder, appData.border, MSG_SIZ-1);
\r
3371 borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
3373 if(borderBitmap == NULL) { // loading failed, use white
\r
3374 FillRect( hdc, &boardRect, whitePieceBrush );
\r
3377 tmphdc = CreateCompatibleDC(hdc);
\r
3378 hbm = SelectObject(tmphdc, borderBitmap);
\r
3379 if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {
\r
3383 mode = SetStretchBltMode(hdc, COLORONCOLOR);
\r
3384 StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left,
\r
3385 boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3386 SetStretchBltMode(hdc, mode);
\r
3387 SelectObject(tmphdc, hbm);
\r
3392 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3394 int row, column, x, y, square_color, piece_color;
\r
3395 ChessSquare piece;
\r
3397 HDC texture_hdc = NULL;
\r
3399 /* [AS] Initialize background textures if needed */
\r
3400 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3401 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3402 if( backTextureSquareSize != squareSize
\r
3403 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3404 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3405 backTextureSquareSize = squareSize;
\r
3406 RebuildTextureSquareInfo();
\r
3409 texture_hdc = CreateCompatibleDC( hdc );
\r
3412 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3413 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3415 SquareToPos(row, column, &x, &y);
\r
3417 piece = board[row][column];
\r
3419 square_color = ((column + row) % 2) == 1;
\r
3420 if( gameInfo.variant == VariantXiangqi ) {
\r
3421 square_color = !InPalace(row, column);
\r
3422 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3423 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3425 piece_color = (int) piece < (int) BlackPawn;
\r
3428 /* [HGM] holdings file: light square or black */
\r
3429 if(column == BOARD_LEFT-2) {
\r
3430 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3433 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3437 if(column == BOARD_RGHT + 1 ) {
\r
3438 if( row < gameInfo.holdingsSize )
\r
3441 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3445 if(column == BOARD_LEFT-1 ) /* left align */
\r
3446 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3447 else if( column == BOARD_RGHT) /* right align */
\r
3448 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3449 else if( piece == DarkSquare) DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3451 if (appData.monoMode) {
\r
3452 if (piece == EmptySquare) {
\r
3453 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3454 square_color ? WHITENESS : BLACKNESS);
\r
3456 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3459 else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {
\r
3460 /* [AS] Draw the square using a texture bitmap */
\r
3461 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3462 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3463 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3466 squareSize, squareSize,
\r
3469 backTextureSquareInfo[r][c].mode,
\r
3470 backTextureSquareInfo[r][c].x,
\r
3471 backTextureSquareInfo[r][c].y );
\r
3473 SelectObject( texture_hdc, hbm );
\r
3475 if (piece != EmptySquare) {
\r
3476 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3480 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3482 oldBrush = SelectObject(hdc, brush );
\r
3483 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3484 SelectObject(hdc, oldBrush);
\r
3485 if (piece != EmptySquare)
\r
3486 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3491 if( texture_hdc != NULL ) {
\r
3492 DeleteDC( texture_hdc );
\r
3496 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3497 void fputDW(FILE *f, int x)
\r
3499 fputc(x & 255, f);
\r
3500 fputc(x>>8 & 255, f);
\r
3501 fputc(x>>16 & 255, f);
\r
3502 fputc(x>>24 & 255, f);
\r
3505 #define MAX_CLIPS 200 /* more than enough */
\r
3508 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3510 // HBITMAP bufferBitmap;
\r
3515 int w = 100, h = 50;
\r
3517 if(logo == NULL) {
\r
3518 if(!logoHeight) return;
\r
3519 FillRect( hdc, &logoRect, whitePieceBrush );
\r
3521 // GetClientRect(hwndMain, &Rect);
\r
3522 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3523 // Rect.bottom-Rect.top+1);
\r
3524 tmphdc = CreateCompatibleDC(hdc);
\r
3525 hbm = SelectObject(tmphdc, logo);
\r
3526 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3530 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3531 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3532 SelectObject(tmphdc, hbm);
\r
3540 HDC hdc = GetDC(hwndMain);
\r
3541 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3542 if(appData.autoLogo) {
\r
3544 switch(gameMode) { // pick logos based on game mode
\r
3545 case IcsObserving:
\r
3546 whiteLogo = second.programLogo; // ICS logo
\r
3547 blackLogo = second.programLogo;
\r
3550 case IcsPlayingWhite:
\r
3551 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3552 blackLogo = second.programLogo; // ICS logo
\r
3554 case IcsPlayingBlack:
\r
3555 whiteLogo = second.programLogo; // ICS logo
\r
3556 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3558 case TwoMachinesPlay:
\r
3559 if(first.twoMachinesColor[0] == 'b') {
\r
3560 whiteLogo = second.programLogo;
\r
3561 blackLogo = first.programLogo;
\r
3564 case MachinePlaysWhite:
\r
3565 blackLogo = userLogo;
\r
3567 case MachinePlaysBlack:
\r
3568 whiteLogo = userLogo;
\r
3569 blackLogo = first.programLogo;
\r
3572 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3573 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3574 ReleaseDC(hwndMain, hdc);
\r
3579 UpdateLogos(int display)
\r
3580 { // called after loading new engine(s), in tourney or from menu
\r
3581 LoadLogo(&first, 0, FALSE);
\r
3582 LoadLogo(&second, 1, appData.icsActive);
\r
3583 InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos
\r
3584 if(display) DisplayLogos();
\r
3587 static HDC hdcSeek;
\r
3589 // [HGM] seekgraph
\r
3590 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3593 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3594 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3595 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3596 SelectObject( hdcSeek, hp );
\r
3599 // front-end wrapper for drawing functions to do rectangles
\r
3600 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3605 if (hdcSeek == NULL) {
\r
3606 hdcSeek = GetDC(hwndMain);
\r
3607 if (!appData.monoMode) {
\r
3608 SelectPalette(hdcSeek, hPal, FALSE);
\r
3609 RealizePalette(hdcSeek);
\r
3612 hp = SelectObject( hdcSeek, gridPen );
\r
3613 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3614 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3615 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3616 SelectObject( hdcSeek, hp );
\r
3619 // front-end wrapper for putting text in graph
\r
3620 void DrawSeekText(char *buf, int x, int y)
\r
3623 SetBkMode( hdcSeek, TRANSPARENT );
\r
3624 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3625 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3628 void DrawSeekDot(int x, int y, int color)
\r
3630 int square = color & 0x80;
\r
3631 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3632 color == 0 ? markerBrush[1] : color == 1 ? darkSquareBrush : explodeBrush);
\r
3635 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3636 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3638 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3639 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3640 SelectObject(hdcSeek, oldBrush);
\r
3643 void DrawSeekOpen()
\r
3647 void DrawSeekClose()
\r
3652 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3654 static Board lastReq[2], lastDrawn[2];
\r
3655 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3656 static int lastDrawnFlipView = 0;
\r
3657 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3658 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3661 HBITMAP bufferBitmap;
\r
3662 HBITMAP oldBitmap;
\r
3664 HRGN clips[MAX_CLIPS];
\r
3665 ChessSquare dragged_piece = EmptySquare;
\r
3666 int nr = twoBoards*partnerUp;
\r
3668 /* I'm undecided on this - this function figures out whether a full
\r
3669 * repaint is necessary on its own, so there's no real reason to have the
\r
3670 * caller tell it that. I think this can safely be set to FALSE - but
\r
3671 * if we trust the callers not to request full repaints unnessesarily, then
\r
3672 * we could skip some clipping work. In other words, only request a full
\r
3673 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3674 * gamestart and similar) --Hawk
\r
3676 Boolean fullrepaint = repaint;
\r
3678 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3680 if( DrawPositionNeedsFullRepaint() ) {
\r
3681 fullrepaint = TRUE;
\r
3684 if (board == NULL) {
\r
3685 if (!lastReqValid[nr]) {
\r
3688 board = lastReq[nr];
\r
3690 CopyBoard(lastReq[nr], board);
\r
3691 lastReqValid[nr] = 1;
\r
3694 if (doingSizing) {
\r
3698 if (IsIconic(hwndMain)) {
\r
3702 if (hdc == NULL) {
\r
3703 hdc = GetDC(hwndMain);
\r
3704 if (!appData.monoMode) {
\r
3705 SelectPalette(hdc, hPal, FALSE);
\r
3706 RealizePalette(hdc);
\r
3710 releaseDC = FALSE;
\r
3713 /* Create some work-DCs */
\r
3714 hdcmem = CreateCompatibleDC(hdc);
\r
3715 tmphdc = CreateCompatibleDC(hdc);
\r
3717 /* If dragging is in progress, we temporarely remove the piece */
\r
3718 /* [HGM] or temporarily decrease count if stacked */
\r
3719 /* !! Moved to before board compare !! */
\r
3720 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3721 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3722 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3723 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3724 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3726 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3727 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3728 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3730 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3733 /* Figure out which squares need updating by comparing the
\r
3734 * newest board with the last drawn board and checking if
\r
3735 * flipping has changed.
\r
3737 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3738 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3739 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3740 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3741 SquareToPos(row, column, &x, &y);
\r
3742 clips[num_clips++] =
\r
3743 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3747 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3748 for (i=0; i<2; i++) {
\r
3749 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3750 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3751 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3752 lastDrawnHighlight.sq[i].y >= 0) {
\r
3753 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3754 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3755 clips[num_clips++] =
\r
3756 CreateRectRgn(x - lineGap, y - lineGap,
\r
3757 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3759 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3760 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3761 clips[num_clips++] =
\r
3762 CreateRectRgn(x - lineGap, y - lineGap,
\r
3763 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3767 for (i=0; i<2; i++) {
\r
3768 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3769 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3770 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3771 lastDrawnPremove.sq[i].y >= 0) {
\r
3772 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3773 lastDrawnPremove.sq[i].x, &x, &y);
\r
3774 clips[num_clips++] =
\r
3775 CreateRectRgn(x - lineGap, y - lineGap,
\r
3776 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3778 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3779 premoveHighlightInfo.sq[i].y >= 0) {
\r
3780 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3781 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3782 clips[num_clips++] =
\r
3783 CreateRectRgn(x - lineGap, y - lineGap,
\r
3784 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3788 } else { // nr == 1
\r
3789 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3790 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3791 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3792 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3793 for (i=0; i<2; i++) {
\r
3794 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3795 partnerHighlightInfo.sq[i].y >= 0) {
\r
3796 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3797 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3798 clips[num_clips++] =
\r
3799 CreateRectRgn(x - lineGap, y - lineGap,
\r
3800 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3802 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3803 oldPartnerHighlight.sq[i].y >= 0) {
\r
3804 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3805 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3806 clips[num_clips++] =
\r
3807 CreateRectRgn(x - lineGap, y - lineGap,
\r
3808 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3813 fullrepaint = TRUE;
\r
3816 /* Create a buffer bitmap - this is the actual bitmap
\r
3817 * being written to. When all the work is done, we can
\r
3818 * copy it to the real DC (the screen). This avoids
\r
3819 * the problems with flickering.
\r
3821 GetClientRect(hwndMain, &Rect);
\r
3822 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3823 Rect.bottom-Rect.top+1);
\r
3824 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3825 if (!appData.monoMode) {
\r
3826 SelectPalette(hdcmem, hPal, FALSE);
\r
3829 /* Create clips for dragging */
\r
3830 if (!fullrepaint) {
\r
3831 if (dragInfo.from.x >= 0) {
\r
3832 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3833 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3835 if (dragInfo.start.x >= 0) {
\r
3836 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3837 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3839 if (dragInfo.pos.x >= 0) {
\r
3840 x = dragInfo.pos.x - squareSize / 2;
\r
3841 y = dragInfo.pos.y - squareSize / 2;
\r
3842 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3844 if (dragInfo.lastpos.x >= 0) {
\r
3845 x = dragInfo.lastpos.x - squareSize / 2;
\r
3846 y = dragInfo.lastpos.y - squareSize / 2;
\r
3847 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3851 /* Are we animating a move?
\r
3853 * - remove the piece from the board (temporarely)
\r
3854 * - calculate the clipping region
\r
3856 if (!fullrepaint) {
\r
3857 if (animInfo.piece != EmptySquare) {
\r
3858 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3859 x = boardRect.left + animInfo.lastpos.x;
\r
3860 y = boardRect.top + animInfo.lastpos.y;
\r
3861 x2 = boardRect.left + animInfo.pos.x;
\r
3862 y2 = boardRect.top + animInfo.pos.y;
\r
3863 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3864 /* Slight kludge. The real problem is that after AnimateMove is
\r
3865 done, the position on the screen does not match lastDrawn.
\r
3866 This currently causes trouble only on e.p. captures in
\r
3867 atomic, where the piece moves to an empty square and then
\r
3868 explodes. The old and new positions both had an empty square
\r
3869 at the destination, but animation has drawn a piece there and
\r
3870 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3871 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3875 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3876 if (num_clips == 0)
\r
3877 fullrepaint = TRUE;
\r
3879 /* Set clipping on the memory DC */
\r
3880 if (!fullrepaint) {
\r
3881 SelectClipRgn(hdcmem, clips[0]);
\r
3882 for (x = 1; x < num_clips; x++) {
\r
3883 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3884 abort(); // this should never ever happen!
\r
3888 /* Do all the drawing to the memory DC */
\r
3889 if(explodeInfo.radius) { // [HGM] atomic
\r
3891 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3892 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3893 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3894 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3895 x += squareSize/2;
\r
3896 y += squareSize/2;
\r
3897 if(!fullrepaint) {
\r
3898 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3899 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3901 DrawGridOnDC(hdcmem);
\r
3902 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3903 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3904 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3905 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3906 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3907 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3908 SelectObject(hdcmem, oldBrush);
\r
3910 if(border) DrawBackgroundOnDC(hdcmem);
\r
3911 DrawGridOnDC(hdcmem);
\r
3912 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3913 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3914 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3916 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3917 oldPartnerHighlight = partnerHighlightInfo;
\r
3919 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3921 if(nr == 0) // [HGM] dual: markers only on left board
\r
3922 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3923 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3924 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3925 HBRUSH oldBrush = SelectObject(hdcmem, markerBrush[marker[row][column]-1]);
\r
3926 SquareToPos(row, column, &x, &y);
\r
3927 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3928 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3929 SelectObject(hdcmem, oldBrush);
\r
3934 if( appData.highlightMoveWithArrow ) {
\r
3935 DrawArrowHighlight(hdcmem);
\r
3938 DrawCoordsOnDC(hdcmem);
\r
3940 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3941 /* to make sure lastDrawn contains what is actually drawn */
\r
3943 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3944 if (dragged_piece != EmptySquare) {
\r
3945 /* [HGM] or restack */
\r
3946 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3947 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3949 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3950 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3951 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3952 x = dragInfo.pos.x - squareSize / 2;
\r
3953 y = dragInfo.pos.y - squareSize / 2;
\r
3954 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
3955 ((int) dragInfo.piece < (int) BlackPawn),
\r
3956 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3959 /* Put the animated piece back into place and draw it */
\r
3960 if (animInfo.piece != EmptySquare) {
\r
3961 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3962 x = boardRect.left + animInfo.pos.x;
\r
3963 y = boardRect.top + animInfo.pos.y;
\r
3964 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3965 ((int) animInfo.piece < (int) BlackPawn),
\r
3966 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3969 /* Release the bufferBitmap by selecting in the old bitmap
\r
3970 * and delete the memory DC
\r
3972 SelectObject(hdcmem, oldBitmap);
\r
3975 /* Set clipping on the target DC */
\r
3976 if (!fullrepaint) {
\r
3977 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3979 GetRgnBox(clips[x], &rect);
\r
3980 DeleteObject(clips[x]);
\r
3981 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3982 rect.right + wpMain.width/2, rect.bottom);
\r
3984 SelectClipRgn(hdc, clips[0]);
\r
3985 for (x = 1; x < num_clips; x++) {
\r
3986 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3987 abort(); // this should never ever happen!
\r
3991 /* Copy the new bitmap onto the screen in one go.
\r
3992 * This way we avoid any flickering
\r
3994 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3995 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3996 boardRect.right - boardRect.left,
\r
3997 boardRect.bottom - boardRect.top,
\r
3998 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3999 if(saveDiagFlag) {
\r
4000 BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData;
\r
4001 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
4003 GetObject(bufferBitmap, sizeof(b), &b);
\r
4004 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\r
4005 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
4006 bih.biWidth = b.bmWidth;
\r
4007 bih.biHeight = b.bmHeight;
\r
4009 bih.biBitCount = b.bmBitsPixel;
\r
4010 bih.biCompression = 0;
\r
4011 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
4012 bih.biXPelsPerMeter = 0;
\r
4013 bih.biYPelsPerMeter = 0;
\r
4014 bih.biClrUsed = 0;
\r
4015 bih.biClrImportant = 0;
\r
4016 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
4017 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
4018 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
4019 // fprintf(diagFile, "%8x\n", (int) pData);
\r
4021 wb = b.bmWidthBytes;
\r
4023 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
4024 int k = ((int*) pData)[i];
\r
4025 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4026 if(j >= 16) break;
\r
4028 if(j >= nrColors) nrColors = j+1;
\r
4030 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
4032 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
4033 for(w=0; w<(wb>>2); w+=2) {
\r
4034 int k = ((int*) pData)[(wb*i>>2) + w];
\r
4035 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4036 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
4037 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
4038 pData[p++] = m | j<<4;
\r
4040 while(p&3) pData[p++] = 0;
\r
4043 wb = ((wb+31)>>5)<<2;
\r
4045 // write BITMAPFILEHEADER
\r
4046 fprintf(diagFile, "BM");
\r
4047 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
4048 fputDW(diagFile, 0);
\r
4049 fputDW(diagFile, 0x36 + (fac?64:0));
\r
4050 // write BITMAPINFOHEADER
\r
4051 fputDW(diagFile, 40);
\r
4052 fputDW(diagFile, b.bmWidth);
\r
4053 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
4054 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
4055 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
4056 fputDW(diagFile, 0);
\r
4057 fputDW(diagFile, 0);
\r
4058 fputDW(diagFile, 0);
\r
4059 fputDW(diagFile, 0);
\r
4060 fputDW(diagFile, 0);
\r
4061 fputDW(diagFile, 0);
\r
4062 // write color table
\r
4064 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
4065 // write bitmap data
\r
4066 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
4067 fputc(pData[i], diagFile);
\r
4072 SelectObject(tmphdc, oldBitmap);
\r
4074 /* Massive cleanup */
\r
4075 for (x = 0; x < num_clips; x++)
\r
4076 DeleteObject(clips[x]);
\r
4079 DeleteObject(bufferBitmap);
\r
4082 ReleaseDC(hwndMain, hdc);
\r
4084 if (lastDrawnFlipView != flipView && nr == 0) {
\r
4086 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
4088 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
4091 /* CopyBoard(lastDrawn, board);*/
\r
4092 lastDrawnHighlight = highlightInfo;
\r
4093 lastDrawnPremove = premoveHighlightInfo;
\r
4094 lastDrawnFlipView = flipView;
\r
4095 lastDrawnValid[nr] = 1;
\r
4098 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
4103 saveDiagFlag = 1; diagFile = f;
\r
4104 HDCDrawPosition(NULL, TRUE, NULL);
\r
4112 /*---------------------------------------------------------------------------*\
\r
4113 | CLIENT PAINT PROCEDURE
\r
4114 | This is the main event-handler for the WM_PAINT message.
\r
4116 \*---------------------------------------------------------------------------*/
\r
4118 PaintProc(HWND hwnd)
\r
4124 if((hdc = BeginPaint(hwnd, &ps))) {
\r
4125 if (IsIconic(hwnd)) {
\r
4126 DrawIcon(hdc, 2, 2, iconCurrent);
\r
4128 if (!appData.monoMode) {
\r
4129 SelectPalette(hdc, hPal, FALSE);
\r
4130 RealizePalette(hdc);
\r
4132 HDCDrawPosition(hdc, 1, NULL);
\r
4133 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
4134 flipView = !flipView; partnerUp = !partnerUp;
\r
4135 HDCDrawPosition(hdc, 1, NULL);
\r
4136 flipView = !flipView; partnerUp = !partnerUp;
\r
4139 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
4140 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
4141 ETO_CLIPPED|ETO_OPAQUE,
\r
4142 &messageRect, messageText, strlen(messageText), NULL);
\r
4143 SelectObject(hdc, oldFont);
\r
4144 DisplayBothClocks();
\r
4147 EndPaint(hwnd,&ps);
\r
4155 * If the user selects on a border boundary, return -1; if off the board,
\r
4156 * return -2. Otherwise map the event coordinate to the square.
\r
4157 * The offset boardRect.left or boardRect.top must already have been
\r
4158 * subtracted from x.
\r
4160 int EventToSquare(x, limit)
\r
4165 if (x < lineGap + border)
\r
4167 x -= lineGap + border;
\r
4168 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4170 x /= (squareSize + lineGap);
\r
4182 DropEnable dropEnables[] = {
\r
4183 { 'P', DP_Pawn, N_("Pawn") },
\r
4184 { 'N', DP_Knight, N_("Knight") },
\r
4185 { 'B', DP_Bishop, N_("Bishop") },
\r
4186 { 'R', DP_Rook, N_("Rook") },
\r
4187 { 'Q', DP_Queen, N_("Queen") },
\r
4191 SetupDropMenu(HMENU hmenu)
\r
4193 int i, count, enable;
\r
4195 extern char white_holding[], black_holding[];
\r
4196 char item[MSG_SIZ];
\r
4198 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4199 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4200 dropEnables[i].piece);
\r
4202 while (p && *p++ == dropEnables[i].piece) count++;
\r
4203 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4204 enable = count > 0 || !appData.testLegality
\r
4205 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4206 && !appData.icsActive);
\r
4207 ModifyMenu(hmenu, dropEnables[i].command,
\r
4208 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4209 dropEnables[i].command, item);
\r
4213 void DragPieceBegin(int x, int y, Boolean instantly)
\r
4215 dragInfo.lastpos.x = boardRect.left + x;
\r
4216 dragInfo.lastpos.y = boardRect.top + y;
\r
4217 if(instantly) dragInfo.pos = dragInfo.lastpos;
\r
4218 dragInfo.from.x = fromX;
\r
4219 dragInfo.from.y = fromY;
\r
4220 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4221 dragInfo.start = dragInfo.from;
\r
4222 SetCapture(hwndMain);
\r
4225 void DragPieceEnd(int x, int y)
\r
4228 dragInfo.start.x = dragInfo.start.y = -1;
\r
4229 dragInfo.from = dragInfo.start;
\r
4230 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4233 void ChangeDragPiece(ChessSquare piece)
\r
4235 dragInfo.piece = piece;
\r
4238 /* Event handler for mouse messages */
\r
4240 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4244 static int recursive = 0;
\r
4246 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4249 if (message == WM_MBUTTONUP) {
\r
4250 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4251 to the middle button: we simulate pressing the left button too!
\r
4253 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4254 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4260 pt.x = LOWORD(lParam);
\r
4261 pt.y = HIWORD(lParam);
\r
4262 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4263 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4264 if (!flipView && y >= 0) {
\r
4265 y = BOARD_HEIGHT - 1 - y;
\r
4267 if (flipView && x >= 0) {
\r
4268 x = BOARD_WIDTH - 1 - x;
\r
4271 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4272 controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status
\r
4274 switch (message) {
\r
4275 case WM_LBUTTONDOWN:
\r
4276 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4277 ClockClick(flipClock); break;
\r
4278 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4279 ClockClick(!flipClock); break;
\r
4281 if(dragging) { // [HGM] lion: don't destroy dragging info if we are already dragging
\r
4282 dragInfo.start.x = dragInfo.start.y = -1;
\r
4283 dragInfo.from = dragInfo.start;
\r
4285 if(fromX == -1 && frozen) { // not sure where this is for
\r
4286 fromX = fromY = -1;
\r
4287 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4290 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4291 DrawPosition(TRUE, NULL);
\r
4294 case WM_LBUTTONUP:
\r
4295 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4296 DrawPosition(TRUE, NULL);
\r
4299 case WM_MOUSEMOVE:
\r
4300 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4301 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4302 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4303 if ((appData.animateDragging || appData.highlightDragging)
\r
4304 && (wParam & MK_LBUTTON || dragging == 2)
\r
4305 && dragInfo.from.x >= 0)
\r
4307 BOOL full_repaint = FALSE;
\r
4309 if (appData.animateDragging) {
\r
4310 dragInfo.pos = pt;
\r
4312 if (appData.highlightDragging) {
\r
4313 HoverEvent(highlightInfo.sq[1].x, highlightInfo.sq[1].y, x, y);
\r
4314 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4315 full_repaint = TRUE;
\r
4319 DrawPosition( full_repaint, NULL);
\r
4321 dragInfo.lastpos = dragInfo.pos;
\r
4325 case WM_MOUSEWHEEL: // [DM]
\r
4326 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4327 /* Mouse Wheel is being rolled forward
\r
4328 * Play moves forward
\r
4330 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4331 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4332 /* Mouse Wheel is being rolled backward
\r
4333 * Play moves backward
\r
4335 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4336 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4340 case WM_MBUTTONUP:
\r
4341 case WM_RBUTTONUP:
\r
4343 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4346 case WM_MBUTTONDOWN:
\r
4347 case WM_RBUTTONDOWN:
\r
4350 fromX = fromY = -1;
\r
4351 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4352 dragInfo.start.x = dragInfo.start.y = -1;
\r
4353 dragInfo.from = dragInfo.start;
\r
4354 dragInfo.lastpos = dragInfo.pos;
\r
4355 if (appData.highlightDragging) {
\r
4356 ClearHighlights();
\r
4359 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4360 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4361 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4362 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4363 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4367 DrawPosition(TRUE, NULL);
\r
4369 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4372 if (message == WM_MBUTTONDOWN) {
\r
4373 buttonCount = 3; /* even if system didn't think so */
\r
4374 if (wParam & MK_SHIFT)
\r
4375 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4377 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4378 } else { /* message == WM_RBUTTONDOWN */
\r
4379 /* Just have one menu, on the right button. Windows users don't
\r
4380 think to try the middle one, and sometimes other software steals
\r
4381 it, or it doesn't really exist. */
\r
4382 if(gameInfo.variant != VariantShogi)
\r
4383 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4385 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4389 SetCapture(hwndMain);
\r
4392 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4393 SetupDropMenu(hmenu);
\r
4394 MenuPopup(hwnd, pt, hmenu, -1);
\r
4404 /* Preprocess messages for buttons in main window */
\r
4406 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4408 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4411 for (i=0; i<N_BUTTONS; i++) {
\r
4412 if (buttonDesc[i].id == id) break;
\r
4414 if (i == N_BUTTONS) return 0;
\r
4415 switch (message) {
\r
4420 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4421 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4428 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4431 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4432 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4433 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4434 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4436 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4438 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4439 TypeInEvent((char)wParam);
\r
4445 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4448 /* Process messages for Promotion dialog box */
\r
4450 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4454 switch (message) {
\r
4455 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4456 /* Center the dialog over the application window */
\r
4457 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4458 Translate(hDlg, DLG_PromotionKing);
\r
4459 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4460 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4461 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4462 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4463 SW_SHOW : SW_HIDE);
\r
4464 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4465 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4466 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4467 PieceToChar(WhiteAngel) != '~') ||
\r
4468 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4469 PieceToChar(BlackAngel) != '~') ) ?
\r
4470 SW_SHOW : SW_HIDE);
\r
4471 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4472 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4473 PieceToChar(WhiteMarshall) != '~') ||
\r
4474 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4475 PieceToChar(BlackMarshall) != '~') ) ?
\r
4476 SW_SHOW : SW_HIDE);
\r
4477 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4478 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4479 gameInfo.variant != VariantShogi ?
\r
4480 SW_SHOW : SW_HIDE);
\r
4481 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4482 gameInfo.variant != VariantShogi ?
\r
4483 SW_SHOW : SW_HIDE);
\r
4484 if(gameInfo.variant == VariantShogi) {
\r
4485 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4486 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4487 SetWindowText(hDlg, "Promote?");
\r
4489 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4490 gameInfo.variant == VariantSuper ?
\r
4491 SW_SHOW : SW_HIDE);
\r
4494 case WM_COMMAND: /* message: received a command */
\r
4495 switch (LOWORD(wParam)) {
\r
4497 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4498 ClearHighlights();
\r
4499 DrawPosition(FALSE, NULL);
\r
4502 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4505 promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4508 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4509 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4512 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4513 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4515 case PB_Chancellor:
\r
4516 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4518 case PB_Archbishop:
\r
4519 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4522 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);
\r
4527 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4528 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4529 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4530 fromX = fromY = -1;
\r
4531 if (!appData.highlightLastMove) {
\r
4532 ClearHighlights();
\r
4533 DrawPosition(FALSE, NULL);
\r
4540 /* Pop up promotion dialog */
\r
4542 PromotionPopup(HWND hwnd)
\r
4546 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4547 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4548 hwnd, (DLGPROC)lpProc);
\r
4549 FreeProcInstance(lpProc);
\r
4555 DrawPosition(TRUE, NULL);
\r
4556 PromotionPopup(hwndMain);
\r
4560 LoadGameDialog(HWND hwnd, char* title)
\r
4564 char fileTitle[MSG_SIZ];
\r
4565 f = OpenFileDialog(hwnd, "rb", "",
\r
4566 appData.oldSaveStyle ? "gam" : "pgn",
\r
4568 title, &number, fileTitle, NULL);
\r
4570 cmailMsgLoaded = FALSE;
\r
4571 if (number == 0) {
\r
4572 int error = GameListBuild(f);
\r
4574 DisplayError(_("Cannot build game list"), error);
\r
4575 } else if (!ListEmpty(&gameList) &&
\r
4576 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4577 GameListPopUp(f, fileTitle);
\r
4580 GameListDestroy();
\r
4583 LoadGame(f, number, fileTitle, FALSE);
\r
4587 int get_term_width()
\r
4592 HFONT hfont, hold_font;
\r
4597 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4601 // get the text metrics
\r
4602 hdc = GetDC(hText);
\r
4603 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4604 if (consoleCF.dwEffects & CFE_BOLD)
\r
4605 lf.lfWeight = FW_BOLD;
\r
4606 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4607 lf.lfItalic = TRUE;
\r
4608 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4609 lf.lfStrikeOut = TRUE;
\r
4610 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4611 lf.lfUnderline = TRUE;
\r
4612 hfont = CreateFontIndirect(&lf);
\r
4613 hold_font = SelectObject(hdc, hfont);
\r
4614 GetTextMetrics(hdc, &tm);
\r
4615 SelectObject(hdc, hold_font);
\r
4616 DeleteObject(hfont);
\r
4617 ReleaseDC(hText, hdc);
\r
4619 // get the rectangle
\r
4620 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4622 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4625 void UpdateICSWidth(HWND hText)
\r
4627 LONG old_width, new_width;
\r
4629 new_width = get_term_width(hText, FALSE);
\r
4630 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4631 if (new_width != old_width)
\r
4633 ics_update_width(new_width);
\r
4634 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4639 ChangedConsoleFont()
\r
4642 CHARRANGE tmpsel, sel;
\r
4643 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4644 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4645 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4648 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4649 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4650 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4651 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4652 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4653 * size. This was undocumented in the version of MSVC++ that I had
\r
4654 * when I wrote the code, but is apparently documented now.
\r
4656 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4657 cfmt.bCharSet = f->lf.lfCharSet;
\r
4658 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4659 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4660 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4661 /* Why are the following seemingly needed too? */
\r
4662 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4663 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4664 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4666 tmpsel.cpMax = -1; /*999999?*/
\r
4667 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4668 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4669 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4670 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4672 paraf.cbSize = sizeof(paraf);
\r
4673 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4674 paraf.dxStartIndent = 0;
\r
4675 paraf.dxOffset = WRAP_INDENT;
\r
4676 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4677 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4678 UpdateICSWidth(hText);
\r
4681 /*---------------------------------------------------------------------------*\
\r
4683 * Window Proc for main window
\r
4685 \*---------------------------------------------------------------------------*/
\r
4687 /* Process messages for main window, etc. */
\r
4689 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4692 int wmId, wmEvent;
\r
4696 char fileTitle[MSG_SIZ];
\r
4697 static SnapData sd;
\r
4698 static int peek=0;
\r
4700 switch (message) {
\r
4702 case WM_PAINT: /* message: repaint portion of window */
\r
4706 case WM_ERASEBKGND:
\r
4707 if (IsIconic(hwnd)) {
\r
4708 /* Cheat; change the message */
\r
4709 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4711 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4715 case WM_LBUTTONDOWN:
\r
4716 case WM_MBUTTONDOWN:
\r
4717 case WM_RBUTTONDOWN:
\r
4718 case WM_LBUTTONUP:
\r
4719 case WM_MBUTTONUP:
\r
4720 case WM_RBUTTONUP:
\r
4721 case WM_MOUSEMOVE:
\r
4722 case WM_MOUSEWHEEL:
\r
4723 MouseEvent(hwnd, message, wParam, lParam);
\r
4727 if((char)wParam == '\b') {
\r
4728 ForwardEvent(); peek = 0;
\r
4731 JAWS_KBUP_NAVIGATION
\r
4736 if((char)wParam == '\b') {
\r
4737 if(!peek) BackwardEvent(), peek = 1;
\r
4740 JAWS_KBDOWN_NAVIGATION
\r
4746 JAWS_ALT_INTERCEPT
\r
4748 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4749 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4750 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4751 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4753 SendMessage(h, message, wParam, lParam);
\r
4754 } else if(lParam != KF_REPEAT) {
\r
4755 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4756 TypeInEvent((char)wParam);
\r
4757 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4758 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4763 case WM_PALETTECHANGED:
\r
4764 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4766 HDC hdc = GetDC(hwndMain);
\r
4767 SelectPalette(hdc, hPal, TRUE);
\r
4768 nnew = RealizePalette(hdc);
\r
4770 paletteChanged = TRUE;
\r
4772 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4774 ReleaseDC(hwnd, hdc);
\r
4778 case WM_QUERYNEWPALETTE:
\r
4779 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4781 HDC hdc = GetDC(hwndMain);
\r
4782 paletteChanged = FALSE;
\r
4783 SelectPalette(hdc, hPal, FALSE);
\r
4784 nnew = RealizePalette(hdc);
\r
4786 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4788 ReleaseDC(hwnd, hdc);
\r
4793 case WM_COMMAND: /* message: command from application menu */
\r
4794 wmId = LOWORD(wParam);
\r
4795 wmEvent = HIWORD(wParam);
\r
4800 SAY("new game enter a move to play against the computer with white");
\r
4803 case IDM_NewGameFRC:
\r
4804 if( NewGameFRC() == 0 ) {
\r
4809 case IDM_NewVariant:
\r
4810 NewVariantPopup(hwnd);
\r
4813 case IDM_LoadGame:
\r
4814 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4817 case IDM_LoadNextGame:
\r
4821 case IDM_LoadPrevGame:
\r
4825 case IDM_ReloadGame:
\r
4829 case IDM_LoadPosition:
\r
4830 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4831 Reset(FALSE, TRUE);
\r
4834 f = OpenFileDialog(hwnd, "rb", "",
\r
4835 appData.oldSaveStyle ? "pos" : "fen",
\r
4837 _("Load Position from File"), &number, fileTitle, NULL);
\r
4839 LoadPosition(f, number, fileTitle);
\r
4843 case IDM_LoadNextPosition:
\r
4844 ReloadPosition(1);
\r
4847 case IDM_LoadPrevPosition:
\r
4848 ReloadPosition(-1);
\r
4851 case IDM_ReloadPosition:
\r
4852 ReloadPosition(0);
\r
4855 case IDM_SaveGame:
\r
4856 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4857 f = OpenFileDialog(hwnd, "a", defName,
\r
4858 appData.oldSaveStyle ? "gam" : "pgn",
\r
4860 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4862 SaveGame(f, 0, "");
\r
4866 case IDM_SavePosition:
\r
4867 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4868 f = OpenFileDialog(hwnd, "a", defName,
\r
4869 appData.oldSaveStyle ? "pos" : "fen",
\r
4871 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4873 SavePosition(f, 0, "");
\r
4877 case IDM_SaveDiagram:
\r
4878 defName = "diagram";
\r
4879 f = OpenFileDialog(hwnd, "wb", defName,
\r
4882 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
4888 case IDM_CreateBook:
\r
4889 CreateBookEvent();
\r
4892 case IDM_CopyGame:
\r
4893 CopyGameToClipboard();
\r
4896 case IDM_PasteGame:
\r
4897 PasteGameFromClipboard();
\r
4900 case IDM_CopyGameListToClipboard:
\r
4901 CopyGameListToClipboard();
\r
4904 /* [AS] Autodetect FEN or PGN data */
\r
4905 case IDM_PasteAny:
\r
4906 PasteGameOrFENFromClipboard();
\r
4909 /* [AS] Move history */
\r
4910 case IDM_ShowMoveHistory:
\r
4911 if( MoveHistoryIsUp() ) {
\r
4912 MoveHistoryPopDown();
\r
4915 MoveHistoryPopUp();
\r
4919 /* [AS] Eval graph */
\r
4920 case IDM_ShowEvalGraph:
\r
4921 if( EvalGraphIsUp() ) {
\r
4922 EvalGraphPopDown();
\r
4926 SetFocus(hwndMain);
\r
4930 /* [AS] Engine output */
\r
4931 case IDM_ShowEngineOutput:
\r
4932 if( EngineOutputIsUp() ) {
\r
4933 EngineOutputPopDown();
\r
4936 EngineOutputPopUp();
\r
4940 /* [AS] User adjudication */
\r
4941 case IDM_UserAdjudication_White:
\r
4942 UserAdjudicationEvent( +1 );
\r
4945 case IDM_UserAdjudication_Black:
\r
4946 UserAdjudicationEvent( -1 );
\r
4949 case IDM_UserAdjudication_Draw:
\r
4950 UserAdjudicationEvent( 0 );
\r
4953 /* [AS] Game list options dialog */
\r
4954 case IDM_GameListOptions:
\r
4955 GameListOptions();
\r
4962 case IDM_CopyPosition:
\r
4963 CopyFENToClipboard();
\r
4966 case IDM_PastePosition:
\r
4967 PasteFENFromClipboard();
\r
4970 case IDM_MailMove:
\r
4974 case IDM_ReloadCMailMsg:
\r
4975 Reset(TRUE, TRUE);
\r
4976 ReloadCmailMsgEvent(FALSE);
\r
4979 case IDM_Minimize:
\r
4980 ShowWindow(hwnd, SW_MINIMIZE);
\r
4987 case IDM_MachineWhite:
\r
4988 MachineWhiteEvent();
\r
4990 * refresh the tags dialog only if it's visible
\r
4992 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4994 tags = PGNTags(&gameInfo);
\r
4995 TagsPopUp(tags, CmailMsg());
\r
4998 SAY("computer starts playing white");
\r
5001 case IDM_MachineBlack:
\r
5002 MachineBlackEvent();
\r
5004 * refresh the tags dialog only if it's visible
\r
5006 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
5008 tags = PGNTags(&gameInfo);
\r
5009 TagsPopUp(tags, CmailMsg());
\r
5012 SAY("computer starts playing black");
\r
5015 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
5016 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
5019 case IDM_TwoMachines:
\r
5020 TwoMachinesEvent();
\r
5022 * refresh the tags dialog only if it's visible
\r
5024 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
5026 tags = PGNTags(&gameInfo);
\r
5027 TagsPopUp(tags, CmailMsg());
\r
5030 SAY("computer starts playing both sides");
\r
5033 case IDM_AnalysisMode:
\r
5034 if(AnalyzeModeEvent()) {
\r
5035 SAY("analyzing current position");
\r
5039 case IDM_AnalyzeFile:
\r
5040 AnalyzeFileEvent();
\r
5043 case IDM_IcsClient:
\r
5047 case IDM_EditGame:
\r
5048 case IDM_EditGame2:
\r
5053 case IDM_EditPosition:
\r
5054 case IDM_EditPosition2:
\r
5055 EditPositionEvent();
\r
5056 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
5059 case IDM_Training:
\r
5063 case IDM_ShowGameList:
\r
5064 ShowGameListProc();
\r
5067 case IDM_EditProgs1:
\r
5068 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
5071 case IDM_LoadProg1:
\r
5072 LoadEnginePopUp(hwndMain, 0);
\r
5075 case IDM_LoadProg2:
\r
5076 LoadEnginePopUp(hwndMain, 1);
\r
5079 case IDM_EditServers:
\r
5080 EditTagsPopUp(icsNames, &icsNames);
\r
5083 case IDM_EditTags:
\r
5088 case IDM_EditBook:
\r
5092 case IDM_EditComment:
\r
5094 if (commentUp && editComment) {
\r
5097 EditCommentEvent();
\r
5118 case IDM_CallFlag:
\r
5138 case IDM_StopObserving:
\r
5139 StopObservingEvent();
\r
5142 case IDM_StopExamining:
\r
5143 StopExaminingEvent();
\r
5147 UploadGameEvent();
\r
5150 case IDM_TypeInMove:
\r
5151 TypeInEvent('\000');
\r
5154 case IDM_TypeInName:
\r
5155 PopUpNameDialog('\000');
\r
5158 case IDM_Backward:
\r
5160 SetFocus(hwndMain);
\r
5167 SetFocus(hwndMain);
\r
5172 SetFocus(hwndMain);
\r
5177 SetFocus(hwndMain);
\r
5180 case OPT_GameListNext: // [HGM] forward these two accelerators to Game List
\r
5181 case OPT_GameListPrev:
\r
5182 if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);
\r
5186 RevertEvent(FALSE);
\r
5189 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5190 RevertEvent(TRUE);
\r
5193 case IDM_TruncateGame:
\r
5194 TruncateGameEvent();
\r
5201 case IDM_RetractMove:
\r
5202 RetractMoveEvent();
\r
5205 case IDM_FlipView:
\r
5206 flipView = !flipView;
\r
5207 DrawPosition(FALSE, NULL);
\r
5210 case IDM_FlipClock:
\r
5211 flipClock = !flipClock;
\r
5212 DisplayBothClocks();
\r
5216 case IDM_MuteSounds:
\r
5217 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5218 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5219 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5222 case IDM_GeneralOptions:
\r
5223 GeneralOptionsPopup(hwnd);
\r
5224 DrawPosition(TRUE, NULL);
\r
5227 case IDM_BoardOptions:
\r
5228 BoardOptionsPopup(hwnd);
\r
5231 case IDM_ThemeOptions:
\r
5232 ThemeOptionsPopup(hwnd);
\r
5235 case IDM_EnginePlayOptions:
\r
5236 EnginePlayOptionsPopup(hwnd);
\r
5239 case IDM_Engine1Options:
\r
5240 EngineOptionsPopup(hwnd, &first);
\r
5243 case IDM_Engine2Options:
\r
5245 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5246 EngineOptionsPopup(hwnd, &second);
\r
5249 case IDM_OptionsUCI:
\r
5250 UciOptionsPopup(hwnd);
\r
5254 TourneyPopup(hwnd);
\r
5257 case IDM_IcsOptions:
\r
5258 IcsOptionsPopup(hwnd);
\r
5262 FontsOptionsPopup(hwnd);
\r
5266 SoundOptionsPopup(hwnd);
\r
5269 case IDM_CommPort:
\r
5270 CommPortOptionsPopup(hwnd);
\r
5273 case IDM_LoadOptions:
\r
5274 LoadOptionsPopup(hwnd);
\r
5277 case IDM_SaveOptions:
\r
5278 SaveOptionsPopup(hwnd);
\r
5281 case IDM_TimeControl:
\r
5282 TimeControlOptionsPopup(hwnd);
\r
5285 case IDM_SaveSettings:
\r
5286 SaveSettings(settingsFileName);
\r
5289 case IDM_SaveSettingsOnExit:
\r
5290 saveSettingsOnExit = !saveSettingsOnExit;
\r
5291 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5292 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5293 MF_CHECKED : MF_UNCHECKED));
\r
5304 case IDM_AboutGame:
\r
5309 appData.debugMode = !appData.debugMode;
\r
5310 if (appData.debugMode) {
\r
5311 char dir[MSG_SIZ];
\r
5312 GetCurrentDirectory(MSG_SIZ, dir);
\r
5313 SetCurrentDirectory(installDir);
\r
5314 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5315 SetCurrentDirectory(dir);
\r
5316 setbuf(debugFP, NULL);
\r
5323 case IDM_HELPCONTENTS:
\r
5324 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5325 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5326 MessageBox (GetFocus(),
\r
5327 _("Unable to activate help"),
\r
5328 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5332 case IDM_HELPSEARCH:
\r
5333 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5334 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5335 MessageBox (GetFocus(),
\r
5336 _("Unable to activate help"),
\r
5337 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5341 case IDM_HELPHELP:
\r
5342 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5343 MessageBox (GetFocus(),
\r
5344 _("Unable to activate help"),
\r
5345 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5350 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5352 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5353 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5354 FreeProcInstance(lpProc);
\r
5357 case IDM_DirectCommand1:
\r
5358 AskQuestionEvent(_("Direct Command"),
\r
5359 _("Send to chess program:"), "", "1");
\r
5361 case IDM_DirectCommand2:
\r
5362 AskQuestionEvent(_("Direct Command"),
\r
5363 _("Send to second chess program:"), "", "2");
\r
5366 case EP_WhitePawn:
\r
5367 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5368 fromX = fromY = -1;
\r
5371 case EP_WhiteKnight:
\r
5372 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5373 fromX = fromY = -1;
\r
5376 case EP_WhiteBishop:
\r
5377 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5378 fromX = fromY = -1;
\r
5381 case EP_WhiteRook:
\r
5382 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5383 fromX = fromY = -1;
\r
5386 case EP_WhiteQueen:
\r
5387 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5388 fromX = fromY = -1;
\r
5391 case EP_WhiteFerz:
\r
5392 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5393 fromX = fromY = -1;
\r
5396 case EP_WhiteWazir:
\r
5397 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5398 fromX = fromY = -1;
\r
5401 case EP_WhiteAlfil:
\r
5402 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5403 fromX = fromY = -1;
\r
5406 case EP_WhiteCannon:
\r
5407 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5408 fromX = fromY = -1;
\r
5411 case EP_WhiteCardinal:
\r
5412 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5413 fromX = fromY = -1;
\r
5416 case EP_WhiteMarshall:
\r
5417 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5418 fromX = fromY = -1;
\r
5421 case EP_WhiteKing:
\r
5422 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5423 fromX = fromY = -1;
\r
5426 case EP_BlackPawn:
\r
5427 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5428 fromX = fromY = -1;
\r
5431 case EP_BlackKnight:
\r
5432 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5433 fromX = fromY = -1;
\r
5436 case EP_BlackBishop:
\r
5437 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5438 fromX = fromY = -1;
\r
5441 case EP_BlackRook:
\r
5442 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5443 fromX = fromY = -1;
\r
5446 case EP_BlackQueen:
\r
5447 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5448 fromX = fromY = -1;
\r
5451 case EP_BlackFerz:
\r
5452 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5453 fromX = fromY = -1;
\r
5456 case EP_BlackWazir:
\r
5457 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5458 fromX = fromY = -1;
\r
5461 case EP_BlackAlfil:
\r
5462 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5463 fromX = fromY = -1;
\r
5466 case EP_BlackCannon:
\r
5467 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5468 fromX = fromY = -1;
\r
5471 case EP_BlackCardinal:
\r
5472 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5473 fromX = fromY = -1;
\r
5476 case EP_BlackMarshall:
\r
5477 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5478 fromX = fromY = -1;
\r
5481 case EP_BlackKing:
\r
5482 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5483 fromX = fromY = -1;
\r
5486 case EP_EmptySquare:
\r
5487 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5488 fromX = fromY = -1;
\r
5491 case EP_ClearBoard:
\r
5492 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5493 fromX = fromY = -1;
\r
5497 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5498 fromX = fromY = -1;
\r
5502 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5503 fromX = fromY = -1;
\r
5507 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5508 fromX = fromY = -1;
\r
5512 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5513 fromX = fromY = -1;
\r
5517 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5518 fromX = fromY = -1;
\r
5522 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5523 fromX = fromY = -1;
\r
5527 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5528 fromX = fromY = -1;
\r
5532 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5533 fromX = fromY = -1;
\r
5537 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5538 fromX = fromY = -1;
\r
5542 barbaric = 0; appData.language = "";
\r
5543 TranslateMenus(0);
\r
5544 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5545 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5546 lastChecked = wmId;
\r
5550 if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)
\r
5551 RecentEngineEvent(wmId - IDM_RecentEngines);
\r
5553 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5554 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5555 TranslateMenus(0);
\r
5556 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5557 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5558 lastChecked = wmId;
\r
5561 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5567 case CLOCK_TIMER_ID:
\r
5568 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5569 clockTimerEvent = 0;
\r
5570 DecrementClocks(); /* call into back end */
\r
5572 case LOAD_GAME_TIMER_ID:
\r
5573 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5574 loadGameTimerEvent = 0;
\r
5575 AutoPlayGameLoop(); /* call into back end */
\r
5577 case ANALYSIS_TIMER_ID:
\r
5578 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5579 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5580 AnalysisPeriodicEvent(0);
\r
5582 KillTimer(hwnd, analysisTimerEvent);
\r
5583 analysisTimerEvent = 0;
\r
5586 case DELAYED_TIMER_ID:
\r
5587 KillTimer(hwnd, delayedTimerEvent);
\r
5588 delayedTimerEvent = 0;
\r
5589 delayedTimerCallback();
\r
5594 case WM_USER_Input:
\r
5595 InputEvent(hwnd, message, wParam, lParam);
\r
5598 /* [AS] Also move "attached" child windows */
\r
5599 case WM_WINDOWPOSCHANGING:
\r
5601 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5602 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5604 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5605 /* Window is moving */
\r
5608 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5609 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5610 rcMain.right = wpMain.x + wpMain.width;
\r
5611 rcMain.top = wpMain.y;
\r
5612 rcMain.bottom = wpMain.y + wpMain.height;
\r
5614 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5615 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5616 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5617 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5618 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5619 wpMain.x = lpwp->x;
\r
5620 wpMain.y = lpwp->y;
\r
5625 /* [AS] Snapping */
\r
5626 case WM_ENTERSIZEMOVE:
\r
5627 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5628 if (hwnd == hwndMain) {
\r
5629 doingSizing = TRUE;
\r
5632 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5636 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5637 if (hwnd == hwndMain) {
\r
5638 lastSizing = wParam;
\r
5643 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5644 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5646 case WM_EXITSIZEMOVE:
\r
5647 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5648 if (hwnd == hwndMain) {
\r
5650 doingSizing = FALSE;
\r
5651 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5652 GetClientRect(hwnd, &client);
\r
5653 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5655 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5657 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5660 case WM_DESTROY: /* message: window being destroyed */
\r
5661 PostQuitMessage(0);
\r
5665 if (hwnd == hwndMain) {
\r
5670 default: /* Passes it on if unprocessed */
\r
5671 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5676 /*---------------------------------------------------------------------------*\
\r
5678 * Misc utility routines
\r
5680 \*---------------------------------------------------------------------------*/
\r
5683 * Decent random number generator, at least not as bad as Windows
\r
5684 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5686 unsigned int randstate;
\r
5691 randstate = randstate * 1664525 + 1013904223;
\r
5692 return (int) randstate & 0x7fffffff;
\r
5696 mysrandom(unsigned int seed)
\r
5703 * returns TRUE if user selects a different color, FALSE otherwise
\r
5707 ChangeColor(HWND hwnd, COLORREF *which)
\r
5709 static BOOL firstTime = TRUE;
\r
5710 static DWORD customColors[16];
\r
5712 COLORREF newcolor;
\r
5717 /* Make initial colors in use available as custom colors */
\r
5718 /* Should we put the compiled-in defaults here instead? */
\r
5720 customColors[i++] = lightSquareColor & 0xffffff;
\r
5721 customColors[i++] = darkSquareColor & 0xffffff;
\r
5722 customColors[i++] = whitePieceColor & 0xffffff;
\r
5723 customColors[i++] = blackPieceColor & 0xffffff;
\r
5724 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5725 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5727 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5728 customColors[i++] = textAttribs[ccl].color;
\r
5730 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5731 firstTime = FALSE;
\r
5734 cc.lStructSize = sizeof(cc);
\r
5735 cc.hwndOwner = hwnd;
\r
5736 cc.hInstance = NULL;
\r
5737 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5738 cc.lpCustColors = (LPDWORD) customColors;
\r
5739 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5741 if (!ChooseColor(&cc)) return FALSE;
\r
5743 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5744 if (newcolor == *which) return FALSE;
\r
5745 *which = newcolor;
\r
5749 InitDrawingColors();
\r
5750 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5755 MyLoadSound(MySound *ms)
\r
5761 if (ms->data && ms->flag) free(ms->data);
\r
5764 switch (ms->name[0]) {
\r
5770 /* System sound from Control Panel. Don't preload here. */
\r
5774 if (ms->name[1] == NULLCHAR) {
\r
5775 /* "!" alone = silence */
\r
5778 /* Builtin wave resource. Error if not found. */
\r
5779 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5780 if (h == NULL) break;
\r
5781 ms->data = (void *)LoadResource(hInst, h);
\r
5782 ms->flag = 0; // not maloced, so cannot be freed!
\r
5783 if (h == NULL) break;
\r
5788 /* .wav file. Error if not found. */
\r
5789 f = fopen(ms->name, "rb");
\r
5790 if (f == NULL) break;
\r
5791 if (fstat(fileno(f), &st) < 0) break;
\r
5792 ms->data = malloc(st.st_size);
\r
5794 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5800 char buf[MSG_SIZ];
\r
5801 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5802 DisplayError(buf, GetLastError());
\r
5808 MyPlaySound(MySound *ms)
\r
5810 BOOLEAN ok = FALSE;
\r
5812 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5813 switch (ms->name[0]) {
\r
5815 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5820 /* System sound from Control Panel (deprecated feature).
\r
5821 "$" alone or an unset sound name gets default beep (still in use). */
\r
5822 if (ms->name[1]) {
\r
5823 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5825 if (!ok) ok = MessageBeep(MB_OK);
\r
5828 /* Builtin wave resource, or "!" alone for silence */
\r
5829 if (ms->name[1]) {
\r
5830 if (ms->data == NULL) return FALSE;
\r
5831 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5837 /* .wav file. Error if not found. */
\r
5838 if (ms->data == NULL) return FALSE;
\r
5839 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5842 /* Don't print an error: this can happen innocently if the sound driver
\r
5843 is busy; for instance, if another instance of WinBoard is playing
\r
5844 a sound at about the same time. */
\r
5850 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5853 OPENFILENAME *ofn;
\r
5854 static UINT *number; /* gross that this is static */
\r
5856 switch (message) {
\r
5857 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5858 /* Center the dialog over the application window */
\r
5859 ofn = (OPENFILENAME *) lParam;
\r
5860 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5861 number = (UINT *) ofn->lCustData;
\r
5862 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5866 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5867 Translate(hDlg, 1536);
\r
5868 return FALSE; /* Allow for further processing */
\r
5871 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5872 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5874 return FALSE; /* Allow for further processing */
\r
5880 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5882 static UINT *number;
\r
5883 OPENFILENAME *ofname;
\r
5886 case WM_INITDIALOG:
\r
5887 Translate(hdlg, DLG_IndexNumber);
\r
5888 ofname = (OPENFILENAME *)lParam;
\r
5889 number = (UINT *)(ofname->lCustData);
\r
5892 ofnot = (OFNOTIFY *)lParam;
\r
5893 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5894 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5903 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5904 char *nameFilt, char *dlgTitle, UINT *number,
\r
5905 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5907 OPENFILENAME openFileName;
\r
5908 char buf1[MSG_SIZ];
\r
5911 if (fileName == NULL) fileName = buf1;
\r
5912 if (defName == NULL) {
\r
5913 safeStrCpy(fileName, "*.", 3 );
\r
5914 strcat(fileName, defExt);
\r
5916 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5918 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5919 if (number) *number = 0;
\r
5921 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5922 openFileName.hwndOwner = hwnd;
\r
5923 openFileName.hInstance = (HANDLE) hInst;
\r
5924 openFileName.lpstrFilter = nameFilt;
\r
5925 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5926 openFileName.nMaxCustFilter = 0L;
\r
5927 openFileName.nFilterIndex = 1L;
\r
5928 openFileName.lpstrFile = fileName;
\r
5929 openFileName.nMaxFile = MSG_SIZ;
\r
5930 openFileName.lpstrFileTitle = fileTitle;
\r
5931 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5932 openFileName.lpstrInitialDir = NULL;
\r
5933 openFileName.lpstrTitle = dlgTitle;
\r
5934 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5935 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5936 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5937 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5938 openFileName.nFileOffset = 0;
\r
5939 openFileName.nFileExtension = 0;
\r
5940 openFileName.lpstrDefExt = defExt;
\r
5941 openFileName.lCustData = (LONG) number;
\r
5942 openFileName.lpfnHook = oldDialog ?
\r
5943 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5944 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5946 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5947 GetOpenFileName(&openFileName)) {
\r
5948 /* open the file */
\r
5949 f = fopen(openFileName.lpstrFile, write);
\r
5951 MessageBox(hwnd, _("File open failed"), NULL,
\r
5952 MB_OK|MB_ICONEXCLAMATION);
\r
5956 int err = CommDlgExtendedError();
\r
5957 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5966 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5968 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5971 * Get the first pop-up menu in the menu template. This is the
\r
5972 * menu that TrackPopupMenu displays.
\r
5974 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5975 TranslateOneMenu(10, hmenuTrackPopup);
\r
5977 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5980 * TrackPopup uses screen coordinates, so convert the
\r
5981 * coordinates of the mouse click to screen coordinates.
\r
5983 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5985 /* Draw and track the floating pop-up menu. */
\r
5986 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5987 pt.x, pt.y, 0, hwnd, NULL);
\r
5989 /* Destroy the menu.*/
\r
5990 DestroyMenu(hmenu);
\r
5995 int sizeX, sizeY, newSizeX, newSizeY;
\r
5997 } ResizeEditPlusButtonsClosure;
\r
6000 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
6002 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
6006 if (hChild == cl->hText) return TRUE;
\r
6007 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
6008 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
6009 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
6010 ScreenToClient(cl->hDlg, &pt);
\r
6011 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
6012 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
6016 /* Resize a dialog that has a (rich) edit field filling most of
\r
6017 the top, with a row of buttons below */
\r
6019 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
6022 int newTextHeight, newTextWidth;
\r
6023 ResizeEditPlusButtonsClosure cl;
\r
6025 /*if (IsIconic(hDlg)) return;*/
\r
6026 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
6028 cl.hdwp = BeginDeferWindowPos(8);
\r
6030 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
6031 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6032 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6033 if (newTextHeight < 0) {
\r
6034 newSizeY += -newTextHeight;
\r
6035 newTextHeight = 0;
\r
6037 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
6038 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6044 cl.newSizeX = newSizeX;
\r
6045 cl.newSizeY = newSizeY;
\r
6046 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
6048 EndDeferWindowPos(cl.hdwp);
\r
6051 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
6053 RECT rChild, rParent;
\r
6054 int wChild, hChild, wParent, hParent;
\r
6055 int wScreen, hScreen, xNew, yNew;
\r
6058 /* Get the Height and Width of the child window */
\r
6059 GetWindowRect (hwndChild, &rChild);
\r
6060 wChild = rChild.right - rChild.left;
\r
6061 hChild = rChild.bottom - rChild.top;
\r
6063 /* Get the Height and Width of the parent window */
\r
6064 GetWindowRect (hwndParent, &rParent);
\r
6065 wParent = rParent.right - rParent.left;
\r
6066 hParent = rParent.bottom - rParent.top;
\r
6068 /* Get the display limits */
\r
6069 hdc = GetDC (hwndChild);
\r
6070 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
6071 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
6072 ReleaseDC(hwndChild, hdc);
\r
6074 /* Calculate new X position, then adjust for screen */
\r
6075 xNew = rParent.left + ((wParent - wChild) /2);
\r
6078 } else if ((xNew+wChild) > wScreen) {
\r
6079 xNew = wScreen - wChild;
\r
6082 /* Calculate new Y position, then adjust for screen */
\r
6084 yNew = rParent.top + ((hParent - hChild) /2);
\r
6087 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
6092 } else if ((yNew+hChild) > hScreen) {
\r
6093 yNew = hScreen - hChild;
\r
6096 /* Set it, and return */
\r
6097 return SetWindowPos (hwndChild, NULL,
\r
6098 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
6101 /* Center one window over another */
\r
6102 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
6104 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
6107 /*---------------------------------------------------------------------------*\
\r
6109 * Startup Dialog functions
\r
6111 \*---------------------------------------------------------------------------*/
\r
6113 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
6115 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6117 while (*cd != NULL) {
\r
6118 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
6124 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
6126 char buf1[MAX_ARG_LEN];
\r
6129 if (str[0] == '@') {
\r
6130 FILE* f = fopen(str + 1, "r");
\r
6132 DisplayFatalError(str + 1, errno, 2);
\r
6135 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
6137 buf1[len] = NULLCHAR;
\r
6141 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6144 char buf[MSG_SIZ];
\r
6145 char *end = strchr(str, '\n');
\r
6146 if (end == NULL) return;
\r
6147 memcpy(buf, str, end - str);
\r
6148 buf[end - str] = NULLCHAR;
\r
6149 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6155 SetStartupDialogEnables(HWND hDlg)
\r
6157 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6158 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6159 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6160 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6161 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6162 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6163 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6164 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6165 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6166 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6167 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6168 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6169 IsDlgButtonChecked(hDlg, OPT_View));
\r
6173 QuoteForFilename(char *filename)
\r
6175 int dquote, space;
\r
6176 dquote = strchr(filename, '"') != NULL;
\r
6177 space = strchr(filename, ' ') != NULL;
\r
6178 if (dquote || space) {
\r
6190 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6192 char buf[MSG_SIZ];
\r
6195 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6196 q = QuoteForFilename(nthcp);
\r
6197 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6198 if (*nthdir != NULLCHAR) {
\r
6199 q = QuoteForFilename(nthdir);
\r
6200 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6202 if (*nthcp == NULLCHAR) {
\r
6203 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6204 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6205 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6206 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6211 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6213 char buf[MSG_SIZ];
\r
6217 switch (message) {
\r
6218 case WM_INITDIALOG:
\r
6219 /* Center the dialog */
\r
6220 CenterWindow (hDlg, GetDesktopWindow());
\r
6221 Translate(hDlg, DLG_Startup);
\r
6222 /* Initialize the dialog items */
\r
6223 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6224 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6225 firstChessProgramNames);
\r
6226 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6227 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6228 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6229 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6230 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6231 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6232 if (*appData.icsHelper != NULLCHAR) {
\r
6233 char *q = QuoteForFilename(appData.icsHelper);
\r
6234 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6236 if (*appData.icsHost == NULLCHAR) {
\r
6237 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6238 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6239 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6240 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6241 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6244 if (appData.icsActive) {
\r
6245 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6247 else if (appData.noChessProgram) {
\r
6248 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6251 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6254 SetStartupDialogEnables(hDlg);
\r
6258 switch (LOWORD(wParam)) {
\r
6260 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6261 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6262 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6264 comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox
\r
6265 ParseArgs(StringGet, &p);
\r
6266 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6267 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6269 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6270 ParseArgs(StringGet, &p);
\r
6271 SwapEngines(singleList); // ... and then make it 'second'
\r
6273 appData.noChessProgram = FALSE;
\r
6274 appData.icsActive = FALSE;
\r
6275 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6276 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6277 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6279 ParseArgs(StringGet, &p);
\r
6280 if (appData.zippyPlay) {
\r
6281 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6282 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6284 ParseArgs(StringGet, &p);
\r
6286 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6287 appData.noChessProgram = TRUE;
\r
6288 appData.icsActive = FALSE;
\r
6290 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6291 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6294 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6295 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6297 ParseArgs(StringGet, &p);
\r
6299 EndDialog(hDlg, TRUE);
\r
6306 case IDM_HELPCONTENTS:
\r
6307 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6308 MessageBox (GetFocus(),
\r
6309 _("Unable to activate help"),
\r
6310 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6315 SetStartupDialogEnables(hDlg);
\r
6323 /*---------------------------------------------------------------------------*\
\r
6325 * About box dialog functions
\r
6327 \*---------------------------------------------------------------------------*/
\r
6329 /* Process messages for "About" dialog box */
\r
6331 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6333 switch (message) {
\r
6334 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6335 /* Center the dialog over the application window */
\r
6336 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6337 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6338 Translate(hDlg, ABOUTBOX);
\r
6342 case WM_COMMAND: /* message: received a command */
\r
6343 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6344 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6345 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6353 /*---------------------------------------------------------------------------*\
\r
6355 * Comment Dialog functions
\r
6357 \*---------------------------------------------------------------------------*/
\r
6360 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6362 static HANDLE hwndText = NULL;
\r
6363 int len, newSizeX, newSizeY, flags;
\r
6364 static int sizeX, sizeY;
\r
6369 switch (message) {
\r
6370 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6371 /* Initialize the dialog items */
\r
6372 Translate(hDlg, DLG_EditComment);
\r
6373 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6374 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6375 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6376 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6377 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6378 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6379 SetWindowText(hDlg, commentTitle);
\r
6380 if (editComment) {
\r
6381 SetFocus(hwndText);
\r
6383 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6385 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6386 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6387 MAKELPARAM(FALSE, 0));
\r
6388 /* Size and position the dialog */
\r
6389 if (!commentDialog) {
\r
6390 commentDialog = hDlg;
\r
6391 flags = SWP_NOZORDER;
\r
6392 GetClientRect(hDlg, &rect);
\r
6393 sizeX = rect.right;
\r
6394 sizeY = rect.bottom;
\r
6395 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6396 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6397 WINDOWPLACEMENT wp;
\r
6398 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6399 wp.length = sizeof(WINDOWPLACEMENT);
\r
6401 wp.showCmd = SW_SHOW;
\r
6402 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6403 wp.rcNormalPosition.left = wpComment.x;
\r
6404 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6405 wp.rcNormalPosition.top = wpComment.y;
\r
6406 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6407 SetWindowPlacement(hDlg, &wp);
\r
6409 GetClientRect(hDlg, &rect);
\r
6410 newSizeX = rect.right;
\r
6411 newSizeY = rect.bottom;
\r
6412 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6413 newSizeX, newSizeY);
\r
6418 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6421 case WM_COMMAND: /* message: received a command */
\r
6422 switch (LOWORD(wParam)) {
\r
6424 if (editComment) {
\r
6426 /* Read changed options from the dialog box */
\r
6427 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6428 len = GetWindowTextLength(hwndText);
\r
6429 str = (char *) malloc(len + 1);
\r
6430 GetWindowText(hwndText, str, len + 1);
\r
6439 ReplaceComment(commentIndex, str);
\r
6446 case OPT_CancelComment:
\r
6450 case OPT_ClearComment:
\r
6451 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6454 case OPT_EditComment:
\r
6455 EditCommentEvent();
\r
6463 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6464 if( wParam == OPT_CommentText ) {
\r
6465 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6467 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6468 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6472 pt.x = LOWORD( lpMF->lParam );
\r
6473 pt.y = HIWORD( lpMF->lParam );
\r
6475 if(lpMF->msg == WM_CHAR) {
\r
6477 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6478 index = sel.cpMin;
\r
6480 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6482 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6483 len = GetWindowTextLength(hwndText);
\r
6484 str = (char *) malloc(len + 1);
\r
6485 GetWindowText(hwndText, str, len + 1);
\r
6486 ReplaceComment(commentIndex, str);
\r
6487 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6488 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6491 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6492 lpMF->msg = WM_USER;
\r
6500 newSizeX = LOWORD(lParam);
\r
6501 newSizeY = HIWORD(lParam);
\r
6502 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6507 case WM_GETMINMAXINFO:
\r
6508 /* Prevent resizing window too small */
\r
6509 mmi = (MINMAXINFO *) lParam;
\r
6510 mmi->ptMinTrackSize.x = 100;
\r
6511 mmi->ptMinTrackSize.y = 100;
\r
6518 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6523 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6525 if (str == NULL) str = "";
\r
6526 p = (char *) malloc(2 * strlen(str) + 2);
\r
6529 if (*str == '\n') *q++ = '\r';
\r
6533 if (commentText != NULL) free(commentText);
\r
6535 commentIndex = index;
\r
6536 commentTitle = title;
\r
6538 editComment = edit;
\r
6540 if (commentDialog) {
\r
6541 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6542 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6544 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6545 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6546 hwndMain, (DLGPROC)lpProc);
\r
6547 FreeProcInstance(lpProc);
\r
6553 /*---------------------------------------------------------------------------*\
\r
6555 * Type-in move dialog functions
\r
6557 \*---------------------------------------------------------------------------*/
\r
6560 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6562 char move[MSG_SIZ];
\r
6565 switch (message) {
\r
6566 case WM_INITDIALOG:
\r
6567 move[0] = (char) lParam;
\r
6568 move[1] = NULLCHAR;
\r
6569 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6570 Translate(hDlg, DLG_TypeInMove);
\r
6571 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6572 SetWindowText(hInput, move);
\r
6574 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6578 switch (LOWORD(wParam)) {
\r
6581 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6582 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6583 TypeInDoneEvent(move);
\r
6584 EndDialog(hDlg, TRUE);
\r
6587 EndDialog(hDlg, FALSE);
\r
6598 PopUpMoveDialog(char firstchar)
\r
6602 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6603 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6604 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6605 FreeProcInstance(lpProc);
\r
6608 /*---------------------------------------------------------------------------*\
\r
6610 * Type-in name dialog functions
\r
6612 \*---------------------------------------------------------------------------*/
\r
6615 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6617 char move[MSG_SIZ];
\r
6620 switch (message) {
\r
6621 case WM_INITDIALOG:
\r
6622 move[0] = (char) lParam;
\r
6623 move[1] = NULLCHAR;
\r
6624 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6625 Translate(hDlg, DLG_TypeInName);
\r
6626 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6627 SetWindowText(hInput, move);
\r
6629 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6633 switch (LOWORD(wParam)) {
\r
6635 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6636 appData.userName = strdup(move);
\r
6639 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6640 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6641 DisplayTitle(move);
\r
6645 EndDialog(hDlg, TRUE);
\r
6648 EndDialog(hDlg, FALSE);
\r
6659 PopUpNameDialog(char firstchar)
\r
6663 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6664 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6665 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6666 FreeProcInstance(lpProc);
\r
6669 /*---------------------------------------------------------------------------*\
\r
6673 \*---------------------------------------------------------------------------*/
\r
6675 /* Nonmodal error box */
\r
6676 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6677 WPARAM wParam, LPARAM lParam);
\r
6680 ErrorPopUp(char *title, char *content)
\r
6684 BOOLEAN modal = hwndMain == NULL;
\r
6702 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6703 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6706 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6708 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6709 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6710 hwndMain, (DLGPROC)lpProc);
\r
6711 FreeProcInstance(lpProc);
\r
6718 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6719 if (errorDialog == NULL) return;
\r
6720 DestroyWindow(errorDialog);
\r
6721 errorDialog = NULL;
\r
6722 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6726 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6731 switch (message) {
\r
6732 case WM_INITDIALOG:
\r
6733 GetWindowRect(hDlg, &rChild);
\r
6736 SetWindowPos(hDlg, NULL, rChild.left,
\r
6737 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6738 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6742 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6743 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6744 and it doesn't work when you resize the dialog.
\r
6745 For now, just give it a default position.
\r
6747 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6748 Translate(hDlg, DLG_Error);
\r
6750 errorDialog = hDlg;
\r
6751 SetWindowText(hDlg, errorTitle);
\r
6752 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6753 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6757 switch (LOWORD(wParam)) {
\r
6760 if (errorDialog == hDlg) errorDialog = NULL;
\r
6761 DestroyWindow(hDlg);
\r
6773 HWND gothicDialog = NULL;
\r
6776 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6780 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6782 switch (message) {
\r
6783 case WM_INITDIALOG:
\r
6784 GetWindowRect(hDlg, &rChild);
\r
6786 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6790 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6791 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6792 and it doesn't work when you resize the dialog.
\r
6793 For now, just give it a default position.
\r
6795 gothicDialog = hDlg;
\r
6796 SetWindowText(hDlg, errorTitle);
\r
6797 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6798 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6802 switch (LOWORD(wParam)) {
\r
6805 if (errorDialog == hDlg) errorDialog = NULL;
\r
6806 DestroyWindow(hDlg);
\r
6818 GothicPopUp(char *title, VariantClass variant)
\r
6821 static char *lastTitle;
\r
6823 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6824 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6826 if(lastTitle != title && gothicDialog != NULL) {
\r
6827 DestroyWindow(gothicDialog);
\r
6828 gothicDialog = NULL;
\r
6830 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6831 title = lastTitle;
\r
6832 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6833 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6834 hwndMain, (DLGPROC)lpProc);
\r
6835 FreeProcInstance(lpProc);
\r
6840 /*---------------------------------------------------------------------------*\
\r
6842 * Ics Interaction console functions
\r
6844 \*---------------------------------------------------------------------------*/
\r
6846 #define HISTORY_SIZE 64
\r
6847 static char *history[HISTORY_SIZE];
\r
6848 int histIn = 0, histP = 0;
\r
6851 SaveInHistory(char *cmd)
\r
6853 if (history[histIn] != NULL) {
\r
6854 free(history[histIn]);
\r
6855 history[histIn] = NULL;
\r
6857 if (*cmd == NULLCHAR) return;
\r
6858 history[histIn] = StrSave(cmd);
\r
6859 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6860 if (history[histIn] != NULL) {
\r
6861 free(history[histIn]);
\r
6863 history[histIn] = NULL;
\r
6869 PrevInHistory(char *cmd)
\r
6872 if (histP == histIn) {
\r
6873 if (history[histIn] != NULL) free(history[histIn]);
\r
6874 history[histIn] = StrSave(cmd);
\r
6876 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6877 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6879 return history[histP];
\r
6885 if (histP == histIn) return NULL;
\r
6886 histP = (histP + 1) % HISTORY_SIZE;
\r
6887 return history[histP];
\r
6891 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6895 hmenu = LoadMenu(hInst, "TextMenu");
\r
6896 h = GetSubMenu(hmenu, 0);
\r
6898 if (strcmp(e->item, "-") == 0) {
\r
6899 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6900 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6901 int flags = MF_STRING, j = 0;
\r
6902 if (e->item[0] == '|') {
\r
6903 flags |= MF_MENUBARBREAK;
\r
6906 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6907 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6915 WNDPROC consoleTextWindowProc;
\r
6918 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6920 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6921 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6925 SetWindowText(hInput, command);
\r
6927 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6929 sel.cpMin = 999999;
\r
6930 sel.cpMax = 999999;
\r
6931 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6936 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6937 if (sel.cpMin == sel.cpMax) {
\r
6938 /* Expand to surrounding word */
\r
6941 tr.chrg.cpMax = sel.cpMin;
\r
6942 tr.chrg.cpMin = --sel.cpMin;
\r
6943 if (sel.cpMin < 0) break;
\r
6944 tr.lpstrText = name;
\r
6945 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6946 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6950 tr.chrg.cpMin = sel.cpMax;
\r
6951 tr.chrg.cpMax = ++sel.cpMax;
\r
6952 tr.lpstrText = name;
\r
6953 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6954 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6957 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6958 MessageBeep(MB_ICONEXCLAMATION);
\r
6962 tr.lpstrText = name;
\r
6963 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6965 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6966 MessageBeep(MB_ICONEXCLAMATION);
\r
6969 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6972 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
6973 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6974 SetWindowText(hInput, buf);
\r
6975 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6977 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6978 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6979 SetWindowText(hInput, buf);
\r
6980 sel.cpMin = 999999;
\r
6981 sel.cpMax = 999999;
\r
6982 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6988 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6993 switch (message) {
\r
6995 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6996 if(wParam=='R') return 0;
\r
6999 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
7002 sel.cpMin = 999999;
\r
7003 sel.cpMax = 999999;
\r
7004 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7005 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
7010 if(wParam != '\022') {
\r
7011 if (wParam == '\t') {
\r
7012 if (GetKeyState(VK_SHIFT) < 0) {
\r
7014 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7015 if (buttonDesc[0].hwnd) {
\r
7016 SetFocus(buttonDesc[0].hwnd);
\r
7018 SetFocus(hwndMain);
\r
7022 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
7025 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7026 JAWS_DELETE( SetFocus(hInput); )
\r
7027 SendMessage(hInput, message, wParam, lParam);
\r
7030 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
7032 case WM_RBUTTONDOWN:
\r
7033 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
7034 /* Move selection here if it was empty */
\r
7036 pt.x = LOWORD(lParam);
\r
7037 pt.y = HIWORD(lParam);
\r
7038 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7039 if (sel.cpMin == sel.cpMax) {
\r
7040 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
7041 sel.cpMax = sel.cpMin;
\r
7042 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7044 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
7045 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
7047 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
7048 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7049 if (sel.cpMin == sel.cpMax) {
\r
7050 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7051 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
7053 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7054 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7056 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
7057 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
7058 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
7059 MenuPopup(hwnd, pt, hmenu, -1);
\r
7063 case WM_RBUTTONUP:
\r
7064 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7065 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7066 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7070 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7072 return SendMessage(hInput, message, wParam, lParam);
\r
7073 case WM_MBUTTONDOWN:
\r
7074 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7076 switch (LOWORD(wParam)) {
\r
7077 case IDM_QuickPaste:
\r
7079 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7080 if (sel.cpMin == sel.cpMax) {
\r
7081 MessageBeep(MB_ICONEXCLAMATION);
\r
7084 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7085 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7086 SendMessage(hInput, WM_PASTE, 0, 0);
\r
7091 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7094 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7097 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7101 int i = LOWORD(wParam) - IDM_CommandX;
\r
7102 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
7103 icsTextMenuEntry[i].command != NULL) {
\r
7104 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
7105 icsTextMenuEntry[i].getname,
\r
7106 icsTextMenuEntry[i].immediate);
\r
7114 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
7117 WNDPROC consoleInputWindowProc;
\r
7120 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7122 char buf[MSG_SIZ];
\r
7124 static BOOL sendNextChar = FALSE;
\r
7125 static BOOL quoteNextChar = FALSE;
\r
7126 InputSource *is = consoleInputSource;
\r
7130 switch (message) {
\r
7132 if (!appData.localLineEditing || sendNextChar) {
\r
7133 is->buf[0] = (CHAR) wParam;
\r
7135 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7136 sendNextChar = FALSE;
\r
7139 if (quoteNextChar) {
\r
7140 buf[0] = (char) wParam;
\r
7141 buf[1] = NULLCHAR;
\r
7142 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7143 quoteNextChar = FALSE;
\r
7147 case '\r': /* Enter key */
\r
7148 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7149 if (consoleEcho) SaveInHistory(is->buf);
\r
7150 is->buf[is->count++] = '\n';
\r
7151 is->buf[is->count] = NULLCHAR;
\r
7152 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7153 if (consoleEcho) {
\r
7154 ConsoleOutput(is->buf, is->count, TRUE);
\r
7155 } else if (appData.localLineEditing) {
\r
7156 ConsoleOutput("\n", 1, TRUE);
\r
7159 case '\033': /* Escape key */
\r
7160 SetWindowText(hwnd, "");
\r
7161 cf.cbSize = sizeof(CHARFORMAT);
\r
7162 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7163 if (consoleEcho) {
\r
7164 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7166 cf.crTextColor = COLOR_ECHOOFF;
\r
7168 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7169 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7171 case '\t': /* Tab key */
\r
7172 if (GetKeyState(VK_SHIFT) < 0) {
\r
7174 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7177 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7178 if (buttonDesc[0].hwnd) {
\r
7179 SetFocus(buttonDesc[0].hwnd);
\r
7181 SetFocus(hwndMain);
\r
7185 case '\023': /* Ctrl+S */
\r
7186 sendNextChar = TRUE;
\r
7188 case '\021': /* Ctrl+Q */
\r
7189 quoteNextChar = TRUE;
\r
7199 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7200 p = PrevInHistory(buf);
\r
7202 SetWindowText(hwnd, p);
\r
7203 sel.cpMin = 999999;
\r
7204 sel.cpMax = 999999;
\r
7205 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7210 p = NextInHistory();
\r
7212 SetWindowText(hwnd, p);
\r
7213 sel.cpMin = 999999;
\r
7214 sel.cpMax = 999999;
\r
7215 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7221 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7225 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7229 case WM_MBUTTONDOWN:
\r
7230 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7231 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7233 case WM_RBUTTONUP:
\r
7234 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7235 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7236 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7240 hmenu = LoadMenu(hInst, "InputMenu");
\r
7241 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7242 if (sel.cpMin == sel.cpMax) {
\r
7243 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7244 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7246 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7247 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7249 pt.x = LOWORD(lParam);
\r
7250 pt.y = HIWORD(lParam);
\r
7251 MenuPopup(hwnd, pt, hmenu, -1);
\r
7255 switch (LOWORD(wParam)) {
\r
7257 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7259 case IDM_SelectAll:
\r
7261 sel.cpMax = -1; /*999999?*/
\r
7262 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7265 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7268 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7271 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7276 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7279 #define CO_MAX 100000
\r
7280 #define CO_TRIM 1000
\r
7283 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7285 static SnapData sd;
\r
7286 HWND hText, hInput;
\r
7288 static int sizeX, sizeY;
\r
7289 int newSizeX, newSizeY;
\r
7293 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7294 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7296 switch (message) {
\r
7298 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7300 ENLINK *pLink = (ENLINK*)lParam;
\r
7301 if (pLink->msg == WM_LBUTTONUP)
\r
7305 tr.chrg = pLink->chrg;
\r
7306 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7307 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7308 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7309 free(tr.lpstrText);
\r
7313 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7314 hwndConsole = hDlg;
\r
7316 consoleTextWindowProc = (WNDPROC)
\r
7317 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7318 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7319 consoleInputWindowProc = (WNDPROC)
\r
7320 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7321 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7322 Colorize(ColorNormal, TRUE);
\r
7323 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7324 ChangedConsoleFont();
\r
7325 GetClientRect(hDlg, &rect);
\r
7326 sizeX = rect.right;
\r
7327 sizeY = rect.bottom;
\r
7328 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7329 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7330 WINDOWPLACEMENT wp;
\r
7331 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7332 wp.length = sizeof(WINDOWPLACEMENT);
\r
7334 wp.showCmd = SW_SHOW;
\r
7335 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7336 wp.rcNormalPosition.left = wpConsole.x;
\r
7337 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7338 wp.rcNormalPosition.top = wpConsole.y;
\r
7339 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7340 SetWindowPlacement(hDlg, &wp);
\r
7343 // [HGM] Chessknight's change 2004-07-13
\r
7344 else { /* Determine Defaults */
\r
7345 WINDOWPLACEMENT wp;
\r
7346 wpConsole.x = wpMain.width + 1;
\r
7347 wpConsole.y = wpMain.y;
\r
7348 wpConsole.width = screenWidth - wpMain.width;
\r
7349 wpConsole.height = wpMain.height;
\r
7350 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7351 wp.length = sizeof(WINDOWPLACEMENT);
\r
7353 wp.showCmd = SW_SHOW;
\r
7354 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7355 wp.rcNormalPosition.left = wpConsole.x;
\r
7356 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7357 wp.rcNormalPosition.top = wpConsole.y;
\r
7358 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7359 SetWindowPlacement(hDlg, &wp);
\r
7362 // Allow hText to highlight URLs and send notifications on them
\r
7363 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7364 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7365 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7366 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7380 if (IsIconic(hDlg)) break;
\r
7381 newSizeX = LOWORD(lParam);
\r
7382 newSizeY = HIWORD(lParam);
\r
7383 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7384 RECT rectText, rectInput;
\r
7386 int newTextHeight, newTextWidth;
\r
7387 GetWindowRect(hText, &rectText);
\r
7388 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7389 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7390 if (newTextHeight < 0) {
\r
7391 newSizeY += -newTextHeight;
\r
7392 newTextHeight = 0;
\r
7394 SetWindowPos(hText, NULL, 0, 0,
\r
7395 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7396 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7397 pt.x = rectInput.left;
\r
7398 pt.y = rectInput.top + newSizeY - sizeY;
\r
7399 ScreenToClient(hDlg, &pt);
\r
7400 SetWindowPos(hInput, NULL,
\r
7401 pt.x, pt.y, /* needs client coords */
\r
7402 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7403 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7409 case WM_GETMINMAXINFO:
\r
7410 /* Prevent resizing window too small */
\r
7411 mmi = (MINMAXINFO *) lParam;
\r
7412 mmi->ptMinTrackSize.x = 100;
\r
7413 mmi->ptMinTrackSize.y = 100;
\r
7416 /* [AS] Snapping */
\r
7417 case WM_ENTERSIZEMOVE:
\r
7418 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7421 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7424 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7426 case WM_EXITSIZEMOVE:
\r
7427 UpdateICSWidth(hText);
\r
7428 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7431 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7439 if (hwndConsole) return;
\r
7440 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7441 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7446 ConsoleOutput(char* data, int length, int forceVisible)
\r
7451 char buf[CO_MAX+1];
\r
7454 static int delayLF = 0;
\r
7455 CHARRANGE savesel, sel;
\r
7457 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7465 while (length--) {
\r
7473 } else if (*p == '\007') {
\r
7474 MyPlaySound(&sounds[(int)SoundBell]);
\r
7481 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7482 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7483 /* Save current selection */
\r
7484 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7485 exlen = GetWindowTextLength(hText);
\r
7486 /* Find out whether current end of text is visible */
\r
7487 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7488 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7489 /* Trim existing text if it's too long */
\r
7490 if (exlen + (q - buf) > CO_MAX) {
\r
7491 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7494 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7495 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7497 savesel.cpMin -= trim;
\r
7498 savesel.cpMax -= trim;
\r
7499 if (exlen < 0) exlen = 0;
\r
7500 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7501 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7503 /* Append the new text */
\r
7504 sel.cpMin = exlen;
\r
7505 sel.cpMax = exlen;
\r
7506 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7507 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7508 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7509 if (forceVisible || exlen == 0 ||
\r
7510 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7511 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7512 /* Scroll to make new end of text visible if old end of text
\r
7513 was visible or new text is an echo of user typein */
\r
7514 sel.cpMin = 9999999;
\r
7515 sel.cpMax = 9999999;
\r
7516 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7517 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7518 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7519 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7521 if (savesel.cpMax == exlen || forceVisible) {
\r
7522 /* Move insert point to new end of text if it was at the old
\r
7523 end of text or if the new text is an echo of user typein */
\r
7524 sel.cpMin = 9999999;
\r
7525 sel.cpMax = 9999999;
\r
7526 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7528 /* Restore previous selection */
\r
7529 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7531 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7538 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7542 COLORREF oldFg, oldBg;
\r
7546 if(copyNumber > 1)
\r
7547 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7549 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7550 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7551 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7554 rect.right = x + squareSize;
\r
7556 rect.bottom = y + squareSize;
\r
7559 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7560 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7561 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7562 &rect, str, strlen(str), NULL);
\r
7564 (void) SetTextColor(hdc, oldFg);
\r
7565 (void) SetBkColor(hdc, oldBg);
\r
7566 (void) SelectObject(hdc, oldFont);
\r
7570 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7571 RECT *rect, char *color, char *flagFell)
\r
7575 COLORREF oldFg, oldBg;
\r
7578 if (twoBoards && partnerUp) return;
\r
7579 if (appData.clockMode) {
\r
7581 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7583 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7590 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7591 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7593 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7594 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7596 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7600 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7601 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7602 rect, str, strlen(str), NULL);
\r
7603 if(logoHeight > 0 && appData.clockMode) {
\r
7605 str += strlen(color)+2;
\r
7606 r.top = rect->top + logoHeight/2;
\r
7607 r.left = rect->left;
\r
7608 r.right = rect->right;
\r
7609 r.bottom = rect->bottom;
\r
7610 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7611 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7612 &r, str, strlen(str), NULL);
\r
7614 (void) SetTextColor(hdc, oldFg);
\r
7615 (void) SetBkColor(hdc, oldBg);
\r
7616 (void) SelectObject(hdc, oldFont);
\r
7621 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7627 if( count <= 0 ) {
\r
7628 if (appData.debugMode) {
\r
7629 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7632 return ERROR_INVALID_USER_BUFFER;
\r
7635 ResetEvent(ovl->hEvent);
\r
7636 ovl->Offset = ovl->OffsetHigh = 0;
\r
7637 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7641 err = GetLastError();
\r
7642 if (err == ERROR_IO_PENDING) {
\r
7643 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7647 err = GetLastError();
\r
7654 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7659 ResetEvent(ovl->hEvent);
\r
7660 ovl->Offset = ovl->OffsetHigh = 0;
\r
7661 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7665 err = GetLastError();
\r
7666 if (err == ERROR_IO_PENDING) {
\r
7667 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7671 err = GetLastError();
\r
7677 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7678 void CheckForInputBufferFull( InputSource * is )
\r
7680 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7681 /* Look for end of line */
\r
7682 char * p = is->buf;
\r
7684 while( p < is->next && *p != '\n' ) {
\r
7688 if( p >= is->next ) {
\r
7689 if (appData.debugMode) {
\r
7690 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7693 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7694 is->count = (DWORD) -1;
\r
7695 is->next = is->buf;
\r
7701 InputThread(LPVOID arg)
\r
7706 is = (InputSource *) arg;
\r
7707 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7708 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7709 while (is->hThread != NULL) {
\r
7710 is->error = DoReadFile(is->hFile, is->next,
\r
7711 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7712 &is->count, &ovl);
\r
7713 if (is->error == NO_ERROR) {
\r
7714 is->next += is->count;
\r
7716 if (is->error == ERROR_BROKEN_PIPE) {
\r
7717 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7720 is->count = (DWORD) -1;
\r
7721 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7726 CheckForInputBufferFull( is );
\r
7728 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7730 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7732 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7735 CloseHandle(ovl.hEvent);
\r
7736 CloseHandle(is->hFile);
\r
7738 if (appData.debugMode) {
\r
7739 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7746 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7748 NonOvlInputThread(LPVOID arg)
\r
7755 is = (InputSource *) arg;
\r
7756 while (is->hThread != NULL) {
\r
7757 is->error = ReadFile(is->hFile, is->next,
\r
7758 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7759 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7760 if (is->error == NO_ERROR) {
\r
7761 /* Change CRLF to LF */
\r
7762 if (is->next > is->buf) {
\r
7764 i = is->count + 1;
\r
7772 if (prev == '\r' && *p == '\n') {
\r
7784 if (is->error == ERROR_BROKEN_PIPE) {
\r
7785 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7788 is->count = (DWORD) -1;
\r
7792 CheckForInputBufferFull( is );
\r
7794 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7796 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7798 if (is->count < 0) break; /* Quit on error */
\r
7800 CloseHandle(is->hFile);
\r
7805 SocketInputThread(LPVOID arg)
\r
7809 is = (InputSource *) arg;
\r
7810 while (is->hThread != NULL) {
\r
7811 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7812 if ((int)is->count == SOCKET_ERROR) {
\r
7813 is->count = (DWORD) -1;
\r
7814 is->error = WSAGetLastError();
\r
7816 is->error = NO_ERROR;
\r
7817 is->next += is->count;
\r
7818 if (is->count == 0 && is->second == is) {
\r
7819 /* End of file on stderr; quit with no message */
\r
7823 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7825 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7827 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7833 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7837 is = (InputSource *) lParam;
\r
7838 if (is->lineByLine) {
\r
7839 /* Feed in lines one by one */
\r
7840 char *p = is->buf;
\r
7842 while (q < is->next) {
\r
7843 if (*q++ == '\n') {
\r
7844 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7849 /* Move any partial line to the start of the buffer */
\r
7851 while (p < is->next) {
\r
7856 if (is->error != NO_ERROR || is->count == 0) {
\r
7857 /* Notify backend of the error. Note: If there was a partial
\r
7858 line at the end, it is not flushed through. */
\r
7859 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7862 /* Feed in the whole chunk of input at once */
\r
7863 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7864 is->next = is->buf;
\r
7868 /*---------------------------------------------------------------------------*\
\r
7870 * Menu enables. Used when setting various modes.
\r
7872 \*---------------------------------------------------------------------------*/
\r
7880 GreyRevert(Boolean grey)
\r
7881 { // [HGM] vari: for retracting variations in local mode
\r
7882 HMENU hmenu = GetMenu(hwndMain);
\r
7883 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7884 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7888 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7890 while (enab->item > 0) {
\r
7891 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7896 Enables gnuEnables[] = {
\r
7897 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7898 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7899 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7900 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7901 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7902 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7903 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7904 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7905 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7906 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7907 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7908 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7909 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7911 // Needed to switch from ncp to GNU mode on Engine Load
\r
7912 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7913 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7914 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7915 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7916 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7917 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7918 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
7919 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7920 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
7921 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
7922 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7923 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7924 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7925 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7929 Enables icsEnables[] = {
\r
7930 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7931 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7932 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7933 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7934 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7935 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7936 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7937 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7938 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7939 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7940 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7941 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7942 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7943 { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },
\r
7944 { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },
\r
7945 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7946 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7947 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7948 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7949 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
7954 Enables zippyEnables[] = {
\r
7955 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7956 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7957 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7958 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7963 Enables ncpEnables[] = {
\r
7964 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7965 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7966 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7967 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7968 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7969 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7970 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7971 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7972 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7973 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7974 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7975 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7976 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7977 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7978 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7979 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7980 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7981 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7982 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7983 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7984 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7985 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
7989 Enables trainingOnEnables[] = {
\r
7990 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7991 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
7992 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7993 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7994 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7995 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7996 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7997 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7998 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
8002 Enables trainingOffEnables[] = {
\r
8003 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
8004 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
8005 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
8006 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
8007 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
8008 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
8009 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
8010 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8011 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
8015 /* These modify either ncpEnables or gnuEnables */
\r
8016 Enables cmailEnables[] = {
\r
8017 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
8018 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
8019 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
8020 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
8021 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
8022 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
8023 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
8027 Enables machineThinkingEnables[] = {
\r
8028 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8029 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
8030 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
8031 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8032 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
8033 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8034 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8035 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8036 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8037 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
8038 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8039 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8040 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8041 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8042 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
8043 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
8047 Enables userThinkingEnables[] = {
\r
8048 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8049 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
8050 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
8051 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8052 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
8053 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8054 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8055 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8056 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8057 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
8058 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
8059 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
8060 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
8061 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
8062 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
8063 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
8067 /*---------------------------------------------------------------------------*\
\r
8069 * Front-end interface functions exported by XBoard.
\r
8070 * Functions appear in same order as prototypes in frontend.h.
\r
8072 \*---------------------------------------------------------------------------*/
\r
8074 CheckMark(UINT item, int state)
\r
8076 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
8082 static UINT prevChecked = 0;
\r
8083 static int prevPausing = 0;
\r
8086 if (pausing != prevPausing) {
\r
8087 prevPausing = pausing;
\r
8088 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
8089 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
8090 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
8093 switch (gameMode) {
\r
8094 case BeginningOfGame:
\r
8095 if (appData.icsActive)
\r
8096 nowChecked = IDM_IcsClient;
\r
8097 else if (appData.noChessProgram)
\r
8098 nowChecked = IDM_EditGame;
\r
8100 nowChecked = IDM_MachineBlack;
\r
8102 case MachinePlaysBlack:
\r
8103 nowChecked = IDM_MachineBlack;
\r
8105 case MachinePlaysWhite:
\r
8106 nowChecked = IDM_MachineWhite;
\r
8108 case TwoMachinesPlay:
\r
8109 nowChecked = IDM_TwoMachines;
\r
8112 nowChecked = IDM_AnalysisMode;
\r
8115 nowChecked = IDM_AnalyzeFile;
\r
8118 nowChecked = IDM_EditGame;
\r
8120 case PlayFromGameFile:
\r
8121 nowChecked = IDM_LoadGame;
\r
8123 case EditPosition:
\r
8124 nowChecked = IDM_EditPosition;
\r
8127 nowChecked = IDM_Training;
\r
8129 case IcsPlayingWhite:
\r
8130 case IcsPlayingBlack:
\r
8131 case IcsObserving:
\r
8133 nowChecked = IDM_IcsClient;
\r
8140 CheckMark(prevChecked, MF_UNCHECKED);
\r
8141 CheckMark(nowChecked, MF_CHECKED);
\r
8142 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8144 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8145 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8146 MF_BYCOMMAND|MF_ENABLED);
\r
8148 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8149 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8152 prevChecked = nowChecked;
\r
8154 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8155 if (appData.icsActive) {
\r
8156 if (appData.icsEngineAnalyze) {
\r
8157 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8159 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8162 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8168 HMENU hmenu = GetMenu(hwndMain);
\r
8169 SetMenuEnables(hmenu, icsEnables);
\r
8170 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8171 MF_BYCOMMAND|MF_ENABLED);
\r
8173 if (appData.zippyPlay) {
\r
8174 SetMenuEnables(hmenu, zippyEnables);
\r
8175 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8176 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8177 MF_BYCOMMAND|MF_ENABLED);
\r
8185 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8191 HMENU hmenu = GetMenu(hwndMain);
\r
8192 SetMenuEnables(hmenu, ncpEnables);
\r
8193 DrawMenuBar(hwndMain);
\r
8199 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8203 SetTrainingModeOn()
\r
8206 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8207 for (i = 0; i < N_BUTTONS; i++) {
\r
8208 if (buttonDesc[i].hwnd != NULL)
\r
8209 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8214 VOID SetTrainingModeOff()
\r
8217 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8218 for (i = 0; i < N_BUTTONS; i++) {
\r
8219 if (buttonDesc[i].hwnd != NULL)
\r
8220 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8226 SetUserThinkingEnables()
\r
8228 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8232 SetMachineThinkingEnables()
\r
8234 HMENU hMenu = GetMenu(hwndMain);
\r
8235 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8237 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8239 if (gameMode == MachinePlaysBlack) {
\r
8240 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8241 } else if (gameMode == MachinePlaysWhite) {
\r
8242 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8243 } else if (gameMode == TwoMachinesPlay) {
\r
8244 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8250 DisplayTitle(char *str)
\r
8252 char title[MSG_SIZ], *host;
\r
8253 if (str[0] != NULLCHAR) {
\r
8254 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8255 } else if (appData.icsActive) {
\r
8256 if (appData.icsCommPort[0] != NULLCHAR)
\r
8259 host = appData.icsHost;
\r
8260 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8261 } else if (appData.noChessProgram) {
\r
8262 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8264 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8265 strcat(title, ": ");
\r
8266 strcat(title, first.tidy);
\r
8268 SetWindowText(hwndMain, title);
\r
8273 DisplayMessage(char *str1, char *str2)
\r
8277 int remain = MESSAGE_TEXT_MAX - 1;
\r
8280 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8281 messageText[0] = NULLCHAR;
\r
8283 len = strlen(str1);
\r
8284 if (len > remain) len = remain;
\r
8285 strncpy(messageText, str1, len);
\r
8286 messageText[len] = NULLCHAR;
\r
8289 if (*str2 && remain >= 2) {
\r
8291 strcat(messageText, " ");
\r
8294 len = strlen(str2);
\r
8295 if (len > remain) len = remain;
\r
8296 strncat(messageText, str2, len);
\r
8298 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8299 safeStrCpy(lastMsg, messageText, MSG_SIZ);
\r
8301 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8305 hdc = GetDC(hwndMain);
\r
8306 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8307 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8308 &messageRect, messageText, strlen(messageText), NULL);
\r
8309 (void) SelectObject(hdc, oldFont);
\r
8310 (void) ReleaseDC(hwndMain, hdc);
\r
8314 DisplayError(char *str, int error)
\r
8316 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8320 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8322 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8323 NULL, error, LANG_NEUTRAL,
\r
8324 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8326 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8328 ErrorMap *em = errmap;
\r
8329 while (em->err != 0 && em->err != error) em++;
\r
8330 if (em->err != 0) {
\r
8331 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8333 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8338 ErrorPopUp(_("Error"), buf);
\r
8343 DisplayMoveError(char *str)
\r
8345 fromX = fromY = -1;
\r
8346 ClearHighlights();
\r
8347 DrawPosition(FALSE, NULL);
\r
8348 if (appData.popupMoveErrors) {
\r
8349 ErrorPopUp(_("Error"), str);
\r
8351 DisplayMessage(str, "");
\r
8352 moveErrorMessageUp = TRUE;
\r
8357 DisplayFatalError(char *str, int error, int exitStatus)
\r
8359 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8361 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8364 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8365 NULL, error, LANG_NEUTRAL,
\r
8366 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8368 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8370 ErrorMap *em = errmap;
\r
8371 while (em->err != 0 && em->err != error) em++;
\r
8372 if (em->err != 0) {
\r
8373 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8375 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8380 if (appData.debugMode) {
\r
8381 fprintf(debugFP, "%s: %s\n", label, str);
\r
8383 if (appData.popupExitMessage) {
\r
8384 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8385 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8387 ExitEvent(exitStatus);
\r
8392 DisplayInformation(char *str)
\r
8394 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8399 DisplayNote(char *str)
\r
8401 ErrorPopUp(_("Note"), str);
\r
8406 char *title, *question, *replyPrefix;
\r
8411 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8413 static QuestionParams *qp;
\r
8414 char reply[MSG_SIZ];
\r
8417 switch (message) {
\r
8418 case WM_INITDIALOG:
\r
8419 qp = (QuestionParams *) lParam;
\r
8420 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8421 Translate(hDlg, DLG_Question);
\r
8422 SetWindowText(hDlg, qp->title);
\r
8423 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8424 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8428 switch (LOWORD(wParam)) {
\r
8430 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8431 if (*reply) strcat(reply, " ");
\r
8432 len = strlen(reply);
\r
8433 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8434 strcat(reply, "\n");
\r
8435 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8436 EndDialog(hDlg, TRUE);
\r
8437 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8440 EndDialog(hDlg, FALSE);
\r
8451 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8453 QuestionParams qp;
\r
8457 qp.question = question;
\r
8458 qp.replyPrefix = replyPrefix;
\r
8460 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8461 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8462 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8463 FreeProcInstance(lpProc);
\r
8466 /* [AS] Pick FRC position */
\r
8467 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8469 static int * lpIndexFRC;
\r
8475 case WM_INITDIALOG:
\r
8476 lpIndexFRC = (int *) lParam;
\r
8478 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8479 Translate(hDlg, DLG_NewGameFRC);
\r
8481 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8482 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8483 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8484 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8489 switch( LOWORD(wParam) ) {
\r
8491 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8492 EndDialog( hDlg, 0 );
\r
8493 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8496 EndDialog( hDlg, 1 );
\r
8498 case IDC_NFG_Edit:
\r
8499 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8500 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8502 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8505 case IDC_NFG_Random:
\r
8506 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8507 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8520 int index = appData.defaultFrcPosition;
\r
8521 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8523 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8525 if( result == 0 ) {
\r
8526 appData.defaultFrcPosition = index;
\r
8532 /* [AS] Game list options. Refactored by HGM */
\r
8534 HWND gameListOptionsDialog;
\r
8536 // low-level front-end: clear text edit / list widget
\r
8540 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8543 // low-level front-end: clear text edit / list widget
\r
8545 GLT_DeSelectList()
\r
8547 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8550 // low-level front-end: append line to text edit / list widget
\r
8552 GLT_AddToList( char *name )
\r
8555 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8559 // low-level front-end: get line from text edit / list widget
\r
8561 GLT_GetFromList( int index, char *name )
\r
8564 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8570 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8572 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8573 int idx2 = idx1 + delta;
\r
8574 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8576 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8579 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8580 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8581 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8582 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8586 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8590 case WM_INITDIALOG:
\r
8591 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8593 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8594 Translate(hDlg, DLG_GameListOptions);
\r
8596 /* Initialize list */
\r
8597 GLT_TagsToList( lpUserGLT );
\r
8599 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8604 switch( LOWORD(wParam) ) {
\r
8607 EndDialog( hDlg, 0 );
\r
8610 EndDialog( hDlg, 1 );
\r
8613 case IDC_GLT_Default:
\r
8614 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8617 case IDC_GLT_Restore:
\r
8618 GLT_TagsToList( appData.gameListTags );
\r
8622 GLT_MoveSelection( hDlg, -1 );
\r
8625 case IDC_GLT_Down:
\r
8626 GLT_MoveSelection( hDlg, +1 );
\r
8636 int GameListOptions()
\r
8639 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8641 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8643 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8645 if( result == 0 ) {
\r
8646 /* [AS] Memory leak here! */
\r
8647 appData.gameListTags = strdup( lpUserGLT );
\r
8654 DisplayIcsInteractionTitle(char *str)
\r
8656 char consoleTitle[MSG_SIZ];
\r
8658 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8659 SetWindowText(hwndConsole, consoleTitle);
\r
8661 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
8662 char buf[MSG_SIZ], *p = buf, *q;
\r
8663 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
8665 q = strchr(p, ';');
\r
8667 if(*p) ChatPopUp(p);
\r
8671 SetActiveWindow(hwndMain);
\r
8675 DrawPosition(int fullRedraw, Board board)
\r
8677 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8680 void NotifyFrontendLogin()
\r
8683 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8689 fromX = fromY = -1;
\r
8690 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8691 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8692 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8693 dragInfo.lastpos = dragInfo.pos;
\r
8694 dragInfo.start.x = dragInfo.start.y = -1;
\r
8695 dragInfo.from = dragInfo.start;
\r
8697 DrawPosition(TRUE, NULL);
\r
8704 CommentPopUp(char *title, char *str)
\r
8706 HWND hwnd = GetActiveWindow();
\r
8707 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8709 SetActiveWindow(hwnd);
\r
8713 CommentPopDown(void)
\r
8715 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8716 if (commentDialog) {
\r
8717 ShowWindow(commentDialog, SW_HIDE);
\r
8719 commentUp = FALSE;
\r
8723 EditCommentPopUp(int index, char *title, char *str)
\r
8725 EitherCommentPopUp(index, title, str, TRUE);
\r
8732 MyPlaySound(&sounds[(int)SoundRoar]);
\r
8739 MyPlaySound(&sounds[(int)SoundMove]);
\r
8742 VOID PlayIcsWinSound()
\r
8744 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8747 VOID PlayIcsLossSound()
\r
8749 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8752 VOID PlayIcsDrawSound()
\r
8754 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8757 VOID PlayIcsUnfinishedSound()
\r
8759 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8765 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8771 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8779 consoleEcho = TRUE;
\r
8780 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8781 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8782 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8791 consoleEcho = FALSE;
\r
8792 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8793 /* This works OK: set text and background both to the same color */
\r
8795 cf.crTextColor = COLOR_ECHOOFF;
\r
8796 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8797 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8800 /* No Raw()...? */
\r
8802 void Colorize(ColorClass cc, int continuation)
\r
8804 currentColorClass = cc;
\r
8805 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8806 consoleCF.crTextColor = textAttribs[cc].color;
\r
8807 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8808 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8814 static char buf[MSG_SIZ];
\r
8815 DWORD bufsiz = MSG_SIZ;
\r
8817 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8818 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8820 if (!GetUserName(buf, &bufsiz)) {
\r
8821 /*DisplayError("Error getting user name", GetLastError());*/
\r
8822 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8830 static char buf[MSG_SIZ];
\r
8831 DWORD bufsiz = MSG_SIZ;
\r
8833 if (!GetComputerName(buf, &bufsiz)) {
\r
8834 /*DisplayError("Error getting host name", GetLastError());*/
\r
8835 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8842 ClockTimerRunning()
\r
8844 return clockTimerEvent != 0;
\r
8850 if (clockTimerEvent == 0) return FALSE;
\r
8851 KillTimer(hwndMain, clockTimerEvent);
\r
8852 clockTimerEvent = 0;
\r
8857 StartClockTimer(long millisec)
\r
8859 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8860 (UINT) millisec, NULL);
\r
8864 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8867 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8869 if(appData.noGUI) return;
\r
8870 hdc = GetDC(hwndMain);
\r
8871 if (!IsIconic(hwndMain)) {
\r
8872 DisplayAClock(hdc, timeRemaining, highlight,
\r
8873 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8875 if (highlight && iconCurrent == iconBlack) {
\r
8876 iconCurrent = iconWhite;
\r
8877 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8878 if (IsIconic(hwndMain)) {
\r
8879 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8882 (void) ReleaseDC(hwndMain, hdc);
\r
8884 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8888 DisplayBlackClock(long timeRemaining, int highlight)
\r
8891 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8894 if(appData.noGUI) return;
\r
8895 hdc = GetDC(hwndMain);
\r
8896 if (!IsIconic(hwndMain)) {
\r
8897 DisplayAClock(hdc, timeRemaining, highlight,
\r
8898 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8900 if (highlight && iconCurrent == iconWhite) {
\r
8901 iconCurrent = iconBlack;
\r
8902 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8903 if (IsIconic(hwndMain)) {
\r
8904 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8907 (void) ReleaseDC(hwndMain, hdc);
\r
8909 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8914 LoadGameTimerRunning()
\r
8916 return loadGameTimerEvent != 0;
\r
8920 StopLoadGameTimer()
\r
8922 if (loadGameTimerEvent == 0) return FALSE;
\r
8923 KillTimer(hwndMain, loadGameTimerEvent);
\r
8924 loadGameTimerEvent = 0;
\r
8929 StartLoadGameTimer(long millisec)
\r
8931 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8932 (UINT) millisec, NULL);
\r
8940 char fileTitle[MSG_SIZ];
\r
8942 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8943 f = OpenFileDialog(hwndMain, "a", defName,
\r
8944 appData.oldSaveStyle ? "gam" : "pgn",
\r
8946 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8948 SaveGame(f, 0, "");
\r
8955 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8957 if (delayedTimerEvent != 0) {
\r
8958 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8959 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8961 KillTimer(hwndMain, delayedTimerEvent);
\r
8962 delayedTimerEvent = 0;
\r
8963 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8964 delayedTimerCallback();
\r
8966 delayedTimerCallback = cb;
\r
8967 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8968 (UINT) millisec, NULL);
\r
8971 DelayedEventCallback
\r
8974 if (delayedTimerEvent) {
\r
8975 return delayedTimerCallback;
\r
8982 CancelDelayedEvent()
\r
8984 if (delayedTimerEvent) {
\r
8985 KillTimer(hwndMain, delayedTimerEvent);
\r
8986 delayedTimerEvent = 0;
\r
8990 DWORD GetWin32Priority(int nice)
\r
8991 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8993 REALTIME_PRIORITY_CLASS 0x00000100
\r
8994 HIGH_PRIORITY_CLASS 0x00000080
\r
8995 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8996 NORMAL_PRIORITY_CLASS 0x00000020
\r
8997 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8998 IDLE_PRIORITY_CLASS 0x00000040
\r
9000 if (nice < -15) return 0x00000080;
\r
9001 if (nice < 0) return 0x00008000;
\r
9002 if (nice == 0) return 0x00000020;
\r
9003 if (nice < 15) return 0x00004000;
\r
9004 return 0x00000040;
\r
9007 void RunCommand(char *cmdLine)
\r
9009 /* Now create the child process. */
\r
9010 STARTUPINFO siStartInfo;
\r
9011 PROCESS_INFORMATION piProcInfo;
\r
9013 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9014 siStartInfo.lpReserved = NULL;
\r
9015 siStartInfo.lpDesktop = NULL;
\r
9016 siStartInfo.lpTitle = NULL;
\r
9017 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9018 siStartInfo.cbReserved2 = 0;
\r
9019 siStartInfo.lpReserved2 = NULL;
\r
9020 siStartInfo.hStdInput = NULL;
\r
9021 siStartInfo.hStdOutput = NULL;
\r
9022 siStartInfo.hStdError = NULL;
\r
9024 CreateProcess(NULL,
\r
9025 cmdLine, /* command line */
\r
9026 NULL, /* process security attributes */
\r
9027 NULL, /* primary thread security attrs */
\r
9028 TRUE, /* handles are inherited */
\r
9029 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9030 NULL, /* use parent's environment */
\r
9032 &siStartInfo, /* STARTUPINFO pointer */
\r
9033 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9035 CloseHandle(piProcInfo.hThread);
\r
9038 /* Start a child process running the given program.
\r
9039 The process's standard output can be read from "from", and its
\r
9040 standard input can be written to "to".
\r
9041 Exit with fatal error if anything goes wrong.
\r
9042 Returns an opaque pointer that can be used to destroy the process
\r
9046 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
9048 #define BUFSIZE 4096
\r
9050 HANDLE hChildStdinRd, hChildStdinWr,
\r
9051 hChildStdoutRd, hChildStdoutWr;
\r
9052 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
9053 SECURITY_ATTRIBUTES saAttr;
\r
9055 PROCESS_INFORMATION piProcInfo;
\r
9056 STARTUPINFO siStartInfo;
\r
9058 char buf[MSG_SIZ];
\r
9061 if (appData.debugMode) {
\r
9062 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
9067 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
9068 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
9069 saAttr.bInheritHandle = TRUE;
\r
9070 saAttr.lpSecurityDescriptor = NULL;
\r
9073 * The steps for redirecting child's STDOUT:
\r
9074 * 1. Create anonymous pipe to be STDOUT for child.
\r
9075 * 2. Create a noninheritable duplicate of read handle,
\r
9076 * and close the inheritable read handle.
\r
9079 /* Create a pipe for the child's STDOUT. */
\r
9080 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
9081 return GetLastError();
\r
9084 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
9085 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
9086 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
9087 FALSE, /* not inherited */
\r
9088 DUPLICATE_SAME_ACCESS);
\r
9090 return GetLastError();
\r
9092 CloseHandle(hChildStdoutRd);
\r
9095 * The steps for redirecting child's STDIN:
\r
9096 * 1. Create anonymous pipe to be STDIN for child.
\r
9097 * 2. Create a noninheritable duplicate of write handle,
\r
9098 * and close the inheritable write handle.
\r
9101 /* Create a pipe for the child's STDIN. */
\r
9102 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
9103 return GetLastError();
\r
9106 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
9107 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
9108 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
9109 FALSE, /* not inherited */
\r
9110 DUPLICATE_SAME_ACCESS);
\r
9112 return GetLastError();
\r
9114 CloseHandle(hChildStdinWr);
\r
9116 /* Arrange to (1) look in dir for the child .exe file, and
\r
9117 * (2) have dir be the child's working directory. Interpret
\r
9118 * dir relative to the directory WinBoard loaded from. */
\r
9119 GetCurrentDirectory(MSG_SIZ, buf);
\r
9120 SetCurrentDirectory(installDir);
\r
9121 SetCurrentDirectory(dir);
\r
9123 /* Now create the child process. */
\r
9125 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9126 siStartInfo.lpReserved = NULL;
\r
9127 siStartInfo.lpDesktop = NULL;
\r
9128 siStartInfo.lpTitle = NULL;
\r
9129 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9130 siStartInfo.cbReserved2 = 0;
\r
9131 siStartInfo.lpReserved2 = NULL;
\r
9132 siStartInfo.hStdInput = hChildStdinRd;
\r
9133 siStartInfo.hStdOutput = hChildStdoutWr;
\r
9134 siStartInfo.hStdError = hChildStdoutWr;
\r
9136 fSuccess = CreateProcess(NULL,
\r
9137 cmdLine, /* command line */
\r
9138 NULL, /* process security attributes */
\r
9139 NULL, /* primary thread security attrs */
\r
9140 TRUE, /* handles are inherited */
\r
9141 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9142 NULL, /* use parent's environment */
\r
9144 &siStartInfo, /* STARTUPINFO pointer */
\r
9145 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9147 err = GetLastError();
\r
9148 SetCurrentDirectory(buf); /* return to prev directory */
\r
9153 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
9154 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
9155 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
9158 /* Close the handles we don't need in the parent */
\r
9159 CloseHandle(piProcInfo.hThread);
\r
9160 CloseHandle(hChildStdinRd);
\r
9161 CloseHandle(hChildStdoutWr);
\r
9163 /* Prepare return value */
\r
9164 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9165 cp->kind = CPReal;
\r
9166 cp->hProcess = piProcInfo.hProcess;
\r
9167 cp->pid = piProcInfo.dwProcessId;
\r
9168 cp->hFrom = hChildStdoutRdDup;
\r
9169 cp->hTo = hChildStdinWrDup;
\r
9171 *pr = (void *) cp;
\r
9173 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
9174 2000 where engines sometimes don't see the initial command(s)
\r
9175 from WinBoard and hang. I don't understand how that can happen,
\r
9176 but the Sleep is harmless, so I've put it in. Others have also
\r
9177 reported what may be the same problem, so hopefully this will fix
\r
9178 it for them too. */
\r
9186 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
9188 ChildProc *cp; int result;
\r
9190 cp = (ChildProc *) pr;
\r
9191 if (cp == NULL) return;
\r
9193 switch (cp->kind) {
\r
9195 /* TerminateProcess is considered harmful, so... */
\r
9196 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9197 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9198 /* The following doesn't work because the chess program
\r
9199 doesn't "have the same console" as WinBoard. Maybe
\r
9200 we could arrange for this even though neither WinBoard
\r
9201 nor the chess program uses a console for stdio? */
\r
9202 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9204 /* [AS] Special termination modes for misbehaving programs... */
\r
9205 if( signal == 9 ) {
\r
9206 result = TerminateProcess( cp->hProcess, 0 );
\r
9208 if ( appData.debugMode) {
\r
9209 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9212 else if( signal == 10 ) {
\r
9213 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
9215 if( dw != WAIT_OBJECT_0 ) {
\r
9216 result = TerminateProcess( cp->hProcess, 0 );
\r
9218 if ( appData.debugMode) {
\r
9219 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9225 CloseHandle(cp->hProcess);
\r
9229 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9233 closesocket(cp->sock);
\r
9238 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9239 closesocket(cp->sock);
\r
9240 closesocket(cp->sock2);
\r
9248 InterruptChildProcess(ProcRef pr)
\r
9252 cp = (ChildProc *) pr;
\r
9253 if (cp == NULL) return;
\r
9254 switch (cp->kind) {
\r
9256 /* The following doesn't work because the chess program
\r
9257 doesn't "have the same console" as WinBoard. Maybe
\r
9258 we could arrange for this even though neither WinBoard
\r
9259 nor the chess program uses a console for stdio */
\r
9260 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9265 /* Can't interrupt */
\r
9269 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9276 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9278 char cmdLine[MSG_SIZ];
\r
9280 if (port[0] == NULLCHAR) {
\r
9281 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9283 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9285 return StartChildProcess(cmdLine, "", pr);
\r
9289 /* Code to open TCP sockets */
\r
9292 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9298 struct sockaddr_in sa, mysa;
\r
9299 struct hostent FAR *hp;
\r
9300 unsigned short uport;
\r
9301 WORD wVersionRequested;
\r
9304 /* Initialize socket DLL */
\r
9305 wVersionRequested = MAKEWORD(1, 1);
\r
9306 err = WSAStartup(wVersionRequested, &wsaData);
\r
9307 if (err != 0) return err;
\r
9310 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9311 err = WSAGetLastError();
\r
9316 /* Bind local address using (mostly) don't-care values.
\r
9318 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9319 mysa.sin_family = AF_INET;
\r
9320 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9321 uport = (unsigned short) 0;
\r
9322 mysa.sin_port = htons(uport);
\r
9323 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9324 == SOCKET_ERROR) {
\r
9325 err = WSAGetLastError();
\r
9330 /* Resolve remote host name */
\r
9331 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9332 if (!(hp = gethostbyname(host))) {
\r
9333 unsigned int b0, b1, b2, b3;
\r
9335 err = WSAGetLastError();
\r
9337 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9338 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9339 hp->h_addrtype = AF_INET;
\r
9341 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9342 hp->h_addr_list[0] = (char *) malloc(4);
\r
9343 hp->h_addr_list[0][0] = (char) b0;
\r
9344 hp->h_addr_list[0][1] = (char) b1;
\r
9345 hp->h_addr_list[0][2] = (char) b2;
\r
9346 hp->h_addr_list[0][3] = (char) b3;
\r
9352 sa.sin_family = hp->h_addrtype;
\r
9353 uport = (unsigned short) atoi(port);
\r
9354 sa.sin_port = htons(uport);
\r
9355 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9357 /* Make connection */
\r
9358 if (connect(s, (struct sockaddr *) &sa,
\r
9359 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9360 err = WSAGetLastError();
\r
9365 /* Prepare return value */
\r
9366 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9367 cp->kind = CPSock;
\r
9369 *pr = (ProcRef *) cp;
\r
9375 OpenCommPort(char *name, ProcRef *pr)
\r
9380 char fullname[MSG_SIZ];
\r
9382 if (*name != '\\')
\r
9383 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9385 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9387 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9388 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9389 if (h == (HANDLE) -1) {
\r
9390 return GetLastError();
\r
9394 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9396 /* Accumulate characters until a 100ms pause, then parse */
\r
9397 ct.ReadIntervalTimeout = 100;
\r
9398 ct.ReadTotalTimeoutMultiplier = 0;
\r
9399 ct.ReadTotalTimeoutConstant = 0;
\r
9400 ct.WriteTotalTimeoutMultiplier = 0;
\r
9401 ct.WriteTotalTimeoutConstant = 0;
\r
9402 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9404 /* Prepare return value */
\r
9405 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9406 cp->kind = CPComm;
\r
9409 *pr = (ProcRef *) cp;
\r
9415 OpenLoopback(ProcRef *pr)
\r
9417 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9423 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9428 struct sockaddr_in sa, mysa;
\r
9429 struct hostent FAR *hp;
\r
9430 unsigned short uport;
\r
9431 WORD wVersionRequested;
\r
9434 char stderrPortStr[MSG_SIZ];
\r
9436 /* Initialize socket DLL */
\r
9437 wVersionRequested = MAKEWORD(1, 1);
\r
9438 err = WSAStartup(wVersionRequested, &wsaData);
\r
9439 if (err != 0) return err;
\r
9441 /* Resolve remote host name */
\r
9442 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9443 if (!(hp = gethostbyname(host))) {
\r
9444 unsigned int b0, b1, b2, b3;
\r
9446 err = WSAGetLastError();
\r
9448 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9449 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9450 hp->h_addrtype = AF_INET;
\r
9452 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9453 hp->h_addr_list[0] = (char *) malloc(4);
\r
9454 hp->h_addr_list[0][0] = (char) b0;
\r
9455 hp->h_addr_list[0][1] = (char) b1;
\r
9456 hp->h_addr_list[0][2] = (char) b2;
\r
9457 hp->h_addr_list[0][3] = (char) b3;
\r
9463 sa.sin_family = hp->h_addrtype;
\r
9464 uport = (unsigned short) 514;
\r
9465 sa.sin_port = htons(uport);
\r
9466 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9468 /* Bind local socket to unused "privileged" port address
\r
9470 s = INVALID_SOCKET;
\r
9471 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9472 mysa.sin_family = AF_INET;
\r
9473 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9474 for (fromPort = 1023;; fromPort--) {
\r
9475 if (fromPort < 0) {
\r
9477 return WSAEADDRINUSE;
\r
9479 if (s == INVALID_SOCKET) {
\r
9480 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9481 err = WSAGetLastError();
\r
9486 uport = (unsigned short) fromPort;
\r
9487 mysa.sin_port = htons(uport);
\r
9488 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9489 == SOCKET_ERROR) {
\r
9490 err = WSAGetLastError();
\r
9491 if (err == WSAEADDRINUSE) continue;
\r
9495 if (connect(s, (struct sockaddr *) &sa,
\r
9496 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9497 err = WSAGetLastError();
\r
9498 if (err == WSAEADDRINUSE) {
\r
9509 /* Bind stderr local socket to unused "privileged" port address
\r
9511 s2 = INVALID_SOCKET;
\r
9512 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9513 mysa.sin_family = AF_INET;
\r
9514 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9515 for (fromPort = 1023;; fromPort--) {
\r
9516 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9517 if (fromPort < 0) {
\r
9518 (void) closesocket(s);
\r
9520 return WSAEADDRINUSE;
\r
9522 if (s2 == INVALID_SOCKET) {
\r
9523 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9524 err = WSAGetLastError();
\r
9530 uport = (unsigned short) fromPort;
\r
9531 mysa.sin_port = htons(uport);
\r
9532 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9533 == SOCKET_ERROR) {
\r
9534 err = WSAGetLastError();
\r
9535 if (err == WSAEADDRINUSE) continue;
\r
9536 (void) closesocket(s);
\r
9540 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9541 err = WSAGetLastError();
\r
9542 if (err == WSAEADDRINUSE) {
\r
9544 s2 = INVALID_SOCKET;
\r
9547 (void) closesocket(s);
\r
9548 (void) closesocket(s2);
\r
9554 prevStderrPort = fromPort; // remember port used
\r
9555 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9557 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9558 err = WSAGetLastError();
\r
9559 (void) closesocket(s);
\r
9560 (void) closesocket(s2);
\r
9565 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9566 err = WSAGetLastError();
\r
9567 (void) closesocket(s);
\r
9568 (void) closesocket(s2);
\r
9572 if (*user == NULLCHAR) user = UserName();
\r
9573 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9574 err = WSAGetLastError();
\r
9575 (void) closesocket(s);
\r
9576 (void) closesocket(s2);
\r
9580 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9581 err = WSAGetLastError();
\r
9582 (void) closesocket(s);
\r
9583 (void) closesocket(s2);
\r
9588 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9589 err = WSAGetLastError();
\r
9590 (void) closesocket(s);
\r
9591 (void) closesocket(s2);
\r
9595 (void) closesocket(s2); /* Stop listening */
\r
9597 /* Prepare return value */
\r
9598 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9599 cp->kind = CPRcmd;
\r
9602 *pr = (ProcRef *) cp;
\r
9609 AddInputSource(ProcRef pr, int lineByLine,
\r
9610 InputCallback func, VOIDSTAR closure)
\r
9612 InputSource *is, *is2 = NULL;
\r
9613 ChildProc *cp = (ChildProc *) pr;
\r
9615 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9616 is->lineByLine = lineByLine;
\r
9618 is->closure = closure;
\r
9619 is->second = NULL;
\r
9620 is->next = is->buf;
\r
9621 if (pr == NoProc) {
\r
9622 is->kind = CPReal;
\r
9623 consoleInputSource = is;
\r
9625 is->kind = cp->kind;
\r
9627 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9628 we create all threads suspended so that the is->hThread variable can be
\r
9629 safely assigned, then let the threads start with ResumeThread.
\r
9631 switch (cp->kind) {
\r
9633 is->hFile = cp->hFrom;
\r
9634 cp->hFrom = NULL; /* now owned by InputThread */
\r
9636 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9637 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9641 is->hFile = cp->hFrom;
\r
9642 cp->hFrom = NULL; /* now owned by InputThread */
\r
9644 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9645 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9649 is->sock = cp->sock;
\r
9651 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9652 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9656 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9658 is->sock = cp->sock;
\r
9660 is2->sock = cp->sock2;
\r
9661 is2->second = is2;
\r
9663 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9664 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9666 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9667 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9671 if( is->hThread != NULL ) {
\r
9672 ResumeThread( is->hThread );
\r
9675 if( is2 != NULL && is2->hThread != NULL ) {
\r
9676 ResumeThread( is2->hThread );
\r
9680 return (InputSourceRef) is;
\r
9684 RemoveInputSource(InputSourceRef isr)
\r
9688 is = (InputSource *) isr;
\r
9689 is->hThread = NULL; /* tell thread to stop */
\r
9690 CloseHandle(is->hThread);
\r
9691 if (is->second != NULL) {
\r
9692 is->second->hThread = NULL;
\r
9693 CloseHandle(is->second->hThread);
\r
9697 int no_wrap(char *message, int count)
\r
9699 ConsoleOutput(message, count, FALSE);
\r
9704 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9707 int outCount = SOCKET_ERROR;
\r
9708 ChildProc *cp = (ChildProc *) pr;
\r
9709 static OVERLAPPED ovl;
\r
9710 static int line = 0;
\r
9714 if (appData.noJoin || !appData.useInternalWrap)
\r
9715 return no_wrap(message, count);
\r
9718 int width = get_term_width();
\r
9719 int len = wrap(NULL, message, count, width, &line);
\r
9720 char *msg = malloc(len);
\r
9724 return no_wrap(message, count);
\r
9727 dbgchk = wrap(msg, message, count, width, &line);
\r
9728 if (dbgchk != len && appData.debugMode)
\r
9729 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9730 ConsoleOutput(msg, len, FALSE);
\r
9737 if (ovl.hEvent == NULL) {
\r
9738 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9740 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9742 switch (cp->kind) {
\r
9745 outCount = send(cp->sock, message, count, 0);
\r
9746 if (outCount == SOCKET_ERROR) {
\r
9747 *outError = WSAGetLastError();
\r
9749 *outError = NO_ERROR;
\r
9754 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9755 &dOutCount, NULL)) {
\r
9756 *outError = NO_ERROR;
\r
9757 outCount = (int) dOutCount;
\r
9759 *outError = GetLastError();
\r
9764 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9765 &dOutCount, &ovl);
\r
9766 if (*outError == NO_ERROR) {
\r
9767 outCount = (int) dOutCount;
\r
9777 if(n != 0) Sleep(n);
\r
9781 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9784 /* Ignore delay, not implemented for WinBoard */
\r
9785 return OutputToProcess(pr, message, count, outError);
\r
9790 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9791 char *buf, int count, int error)
\r
9793 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9796 /* see wgamelist.c for Game List functions */
\r
9797 /* see wedittags.c for Edit Tags functions */
\r
9804 char buf[MSG_SIZ];
\r
9807 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9808 f = fopen(buf, "r");
\r
9810 ProcessICSInitScript(f);
\r
9820 StartAnalysisClock()
\r
9822 if (analysisTimerEvent) return;
\r
9823 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9824 (UINT) 2000, NULL);
\r
9828 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9830 highlightInfo.sq[0].x = fromX;
\r
9831 highlightInfo.sq[0].y = fromY;
\r
9832 highlightInfo.sq[1].x = toX;
\r
9833 highlightInfo.sq[1].y = toY;
\r
9839 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9840 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9844 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9846 premoveHighlightInfo.sq[0].x = fromX;
\r
9847 premoveHighlightInfo.sq[0].y = fromY;
\r
9848 premoveHighlightInfo.sq[1].x = toX;
\r
9849 premoveHighlightInfo.sq[1].y = toY;
\r
9853 ClearPremoveHighlights()
\r
9855 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9856 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9860 ShutDownFrontEnd()
\r
9862 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9863 DeleteClipboardTempFiles();
\r
9869 if (IsIconic(hwndMain))
\r
9870 ShowWindow(hwndMain, SW_RESTORE);
\r
9872 SetActiveWindow(hwndMain);
\r
9876 * Prototypes for animation support routines
\r
9878 static void ScreenSquare(int column, int row, POINT * pt);
\r
9879 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9880 POINT frames[], int * nFrames);
\r
9886 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9887 { // [HGM] atomic: animate blast wave
\r
9890 explodeInfo.fromX = fromX;
\r
9891 explodeInfo.fromY = fromY;
\r
9892 explodeInfo.toX = toX;
\r
9893 explodeInfo.toY = toY;
\r
9894 for(i=1; i<4*kFactor; i++) {
\r
9895 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9896 DrawPosition(FALSE, board);
\r
9897 Sleep(appData.animSpeed);
\r
9899 explodeInfo.radius = 0;
\r
9900 DrawPosition(TRUE, board);
\r
9904 AnimateMove(board, fromX, fromY, toX, toY)
\r
9911 ChessSquare piece;
\r
9912 int x = toX, y = toY;
\r
9913 POINT start, finish, mid;
\r
9914 POINT frames[kFactor * 2 + 1];
\r
9917 if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();
\r
9919 if (!appData.animate) return;
\r
9920 if (doingSizing) return;
\r
9921 if (fromY < 0 || fromX < 0) return;
\r
9922 piece = board[fromY][fromX];
\r
9923 if (piece >= EmptySquare) return;
\r
9925 if(killX >= 0) toX = killX, toY = killY; // [HGM] lion: first to kill square
\r
9929 ScreenSquare(fromX, fromY, &start);
\r
9930 ScreenSquare(toX, toY, &finish);
\r
9932 /* All moves except knight jumps move in straight line */
\r
9933 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9934 mid.x = start.x + (finish.x - start.x) / 2;
\r
9935 mid.y = start.y + (finish.y - start.y) / 2;
\r
9937 /* Knight: make straight movement then diagonal */
\r
9938 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9939 mid.x = start.x + (finish.x - start.x) / 2;
\r
9943 mid.y = start.y + (finish.y - start.y) / 2;
\r
9947 /* Don't use as many frames for very short moves */
\r
9948 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9949 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9951 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9953 animInfo.from.x = fromX;
\r
9954 animInfo.from.y = fromY;
\r
9955 animInfo.to.x = toX;
\r
9956 animInfo.to.y = toY;
\r
9957 animInfo.lastpos = start;
\r
9958 animInfo.piece = piece;
\r
9959 for (n = 0; n < nFrames; n++) {
\r
9960 animInfo.pos = frames[n];
\r
9961 DrawPosition(FALSE, NULL);
\r
9962 animInfo.lastpos = animInfo.pos;
\r
9963 Sleep(appData.animSpeed);
\r
9965 animInfo.pos = finish;
\r
9966 DrawPosition(FALSE, NULL);
\r
9968 if(toX != x || toY != y) { fromX = toX; fromY = toY; toX = x; toY = y; goto again; } // second leg
\r
9970 animInfo.piece = EmptySquare;
\r
9971 Explode(board, fromX, fromY, toX, toY);
\r
9974 /* Convert board position to corner of screen rect and color */
\r
9977 ScreenSquare(column, row, pt)
\r
9978 int column; int row; POINT * pt;
\r
9981 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
9982 pt->y = lineGap + row * (squareSize + lineGap) + border;
\r
9984 pt->x = lineGap + column * (squareSize + lineGap) + border;
\r
9985 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
9989 /* Generate a series of frame coords from start->mid->finish.
\r
9990 The movement rate doubles until the half way point is
\r
9991 reached, then halves back down to the final destination,
\r
9992 which gives a nice slow in/out effect. The algorithmn
\r
9993 may seem to generate too many intermediates for short
\r
9994 moves, but remember that the purpose is to attract the
\r
9995 viewers attention to the piece about to be moved and
\r
9996 then to where it ends up. Too few frames would be less
\r
10000 Tween(start, mid, finish, factor, frames, nFrames)
\r
10001 POINT * start; POINT * mid;
\r
10002 POINT * finish; int factor;
\r
10003 POINT frames[]; int * nFrames;
\r
10005 int n, fraction = 1, count = 0;
\r
10007 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
10008 for (n = 0; n < factor; n++)
\r
10010 for (n = 0; n < factor; n++) {
\r
10011 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
10012 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
10014 fraction = fraction / 2;
\r
10018 frames[count] = *mid;
\r
10021 /* Slow out, stepping 1/2, then 1/4, ... */
\r
10023 for (n = 0; n < factor; n++) {
\r
10024 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
10025 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
10027 fraction = fraction * 2;
\r
10029 *nFrames = count;
\r
10033 SettingsPopUp(ChessProgramState *cps)
\r
10034 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
10035 EngineOptionsPopup(savedHwnd, cps);
\r
10038 int flock(int fid, int code)
\r
10040 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
10042 ov.hEvent = NULL;
\r
10044 ov.OffsetHigh = 0;
\r
10046 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
10047 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
10048 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
10049 default: return -1;
\r
10058 static char col[8][20];
\r
10059 COLORREF color = *(COLORREF *) colorVariable[n];
\r
10061 snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
10066 ActivateTheme (int new)
\r
10067 { // Redo initialization of features depending on options that can occur in themes
\r
10069 if(new) InitDrawingColors();
\r
10070 fontBitmapSquareSize = 0; // request creation of new font pieces
\r
10071 InitDrawingSizes(boardSize, 0);
\r
10072 InvalidateRect(hwndMain, NULL, TRUE);
\r