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 static int promoStyle;
\r
4450 /* Process messages for Promotion dialog box */
\r
4452 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4456 switch (message) {
\r
4457 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4458 /* Center the dialog over the application window */
\r
4459 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4460 Translate(hDlg, DLG_PromotionKing);
\r
4461 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4462 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4463 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4464 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4465 SW_SHOW : SW_HIDE);
\r
4466 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4467 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4468 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4469 PieceToChar(WhiteAngel) != '~') ||
\r
4470 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4471 PieceToChar(BlackAngel) != '~') ) ?
\r
4472 SW_SHOW : SW_HIDE);
\r
4473 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4474 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4475 PieceToChar(WhiteMarshall) != '~') ||
\r
4476 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4477 PieceToChar(BlackMarshall) != '~') ) ?
\r
4478 SW_SHOW : SW_HIDE);
\r
4479 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4480 ShowWindow(GetDlgItem(hDlg, PB_Rook), !style ? SW_SHOW : SW_HIDE);
\r
4481 ShowWindow(GetDlgItem(hDlg, PB_Bishop), !style ? SW_SHOW : SW_HIDE);
\r
4483 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4484 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4485 SetWindowText(hDlg, "Promote?");
\r
4487 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4488 gameInfo.variant == VariantSuper ?
\r
4489 SW_SHOW : SW_HIDE);
\r
4492 case WM_COMMAND: /* message: received a command */
\r
4493 switch (LOWORD(wParam)) {
\r
4495 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4496 ClearHighlights();
\r
4497 DrawPosition(FALSE, NULL);
\r
4500 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4503 promoChar = style ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4506 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4507 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4510 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4511 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4513 case PB_Chancellor:
\r
4514 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4516 case PB_Archbishop:
\r
4517 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4520 promoChar = gameInfo.variant == VariantShogi ? '=' : style ? NULLCHAR :
\r
4521 ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight));
\r
4526 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4527 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4528 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4529 fromX = fromY = -1;
\r
4530 if (!appData.highlightLastMove) {
\r
4531 ClearHighlights();
\r
4532 DrawPosition(FALSE, NULL);
\r
4539 /* Pop up promotion dialog */
\r
4541 PromotionPopup(HWND hwnd)
\r
4545 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4546 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4547 hwnd, (DLGPROC)lpProc);
\r
4548 FreeProcInstance(lpProc);
\r
4552 PromotionPopUp(char choice)
\r
4554 promoStyle = (choice == '+');
\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
6852 SaveInHistory(char *cmd)
\r
6854 if (history[histIn] != NULL) {
\r
6855 free(history[histIn]);
\r
6856 history[histIn] = NULL;
\r
6858 if (*cmd == NULLCHAR) return;
\r
6859 history[histIn] = StrSave(cmd);
\r
6860 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6861 if (history[histIn] != NULL) {
\r
6862 free(history[histIn]);
\r
6864 history[histIn] = NULL;
\r
6870 PrevInHistory(char *cmd)
\r
6873 if (histP == histIn) {
\r
6874 if (history[histIn] != NULL) free(history[histIn]);
\r
6875 history[histIn] = StrSave(cmd);
\r
6877 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6878 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6880 return history[histP];
\r
6886 if (histP == histIn) return NULL;
\r
6887 histP = (histP + 1) % HISTORY_SIZE;
\r
6888 return history[histP];
\r
6892 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6896 hmenu = LoadMenu(hInst, "TextMenu");
\r
6897 h = GetSubMenu(hmenu, 0);
\r
6899 if (strcmp(e->item, "-") == 0) {
\r
6900 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6901 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6902 int flags = MF_STRING, j = 0;
\r
6903 if (e->item[0] == '|') {
\r
6904 flags |= MF_MENUBARBREAK;
\r
6907 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6908 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6916 WNDPROC consoleTextWindowProc;
\r
6919 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6921 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6922 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6926 SetWindowText(hInput, command);
\r
6928 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6930 sel.cpMin = 999999;
\r
6931 sel.cpMax = 999999;
\r
6932 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6937 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6938 if (sel.cpMin == sel.cpMax) {
\r
6939 /* Expand to surrounding word */
\r
6942 tr.chrg.cpMax = sel.cpMin;
\r
6943 tr.chrg.cpMin = --sel.cpMin;
\r
6944 if (sel.cpMin < 0) break;
\r
6945 tr.lpstrText = name;
\r
6946 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6947 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6951 tr.chrg.cpMin = sel.cpMax;
\r
6952 tr.chrg.cpMax = ++sel.cpMax;
\r
6953 tr.lpstrText = name;
\r
6954 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6955 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6958 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6959 MessageBeep(MB_ICONEXCLAMATION);
\r
6963 tr.lpstrText = name;
\r
6964 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6966 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6967 MessageBeep(MB_ICONEXCLAMATION);
\r
6970 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6973 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
6974 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6975 SetWindowText(hInput, buf);
\r
6976 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6978 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6979 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6980 SetWindowText(hInput, buf);
\r
6981 sel.cpMin = 999999;
\r
6982 sel.cpMax = 999999;
\r
6983 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6989 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6994 switch (message) {
\r
6996 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6997 if(wParam=='R') return 0;
\r
7000 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
7003 sel.cpMin = 999999;
\r
7004 sel.cpMax = 999999;
\r
7005 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7006 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
7011 if(wParam != '\022') {
\r
7012 if (wParam == '\t') {
\r
7013 if (GetKeyState(VK_SHIFT) < 0) {
\r
7015 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7016 if (buttonDesc[0].hwnd) {
\r
7017 SetFocus(buttonDesc[0].hwnd);
\r
7019 SetFocus(hwndMain);
\r
7023 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
7026 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7027 JAWS_DELETE( SetFocus(hInput); )
\r
7028 SendMessage(hInput, message, wParam, lParam);
\r
7031 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
7033 case WM_RBUTTONDOWN:
\r
7034 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
7035 /* Move selection here if it was empty */
\r
7037 pt.x = LOWORD(lParam);
\r
7038 pt.y = HIWORD(lParam);
\r
7039 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7040 if (sel.cpMin == sel.cpMax) {
\r
7041 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
7042 sel.cpMax = sel.cpMin;
\r
7043 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7045 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
7046 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
7048 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
7049 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7050 if (sel.cpMin == sel.cpMax) {
\r
7051 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7052 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
7054 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7055 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7057 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
7058 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
7059 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
7060 MenuPopup(hwnd, pt, hmenu, -1);
\r
7064 case WM_RBUTTONUP:
\r
7065 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7066 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7067 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7071 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7073 return SendMessage(hInput, message, wParam, lParam);
\r
7074 case WM_MBUTTONDOWN:
\r
7075 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7077 switch (LOWORD(wParam)) {
\r
7078 case IDM_QuickPaste:
\r
7080 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7081 if (sel.cpMin == sel.cpMax) {
\r
7082 MessageBeep(MB_ICONEXCLAMATION);
\r
7085 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7086 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7087 SendMessage(hInput, WM_PASTE, 0, 0);
\r
7092 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7095 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7098 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7102 int i = LOWORD(wParam) - IDM_CommandX;
\r
7103 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
7104 icsTextMenuEntry[i].command != NULL) {
\r
7105 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
7106 icsTextMenuEntry[i].getname,
\r
7107 icsTextMenuEntry[i].immediate);
\r
7115 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
7118 WNDPROC consoleInputWindowProc;
\r
7121 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7123 char buf[MSG_SIZ];
\r
7125 static BOOL sendNextChar = FALSE;
\r
7126 static BOOL quoteNextChar = FALSE;
\r
7127 InputSource *is = consoleInputSource;
\r
7131 switch (message) {
\r
7133 if (!appData.localLineEditing || sendNextChar) {
\r
7134 is->buf[0] = (CHAR) wParam;
\r
7136 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7137 sendNextChar = FALSE;
\r
7140 if (quoteNextChar) {
\r
7141 buf[0] = (char) wParam;
\r
7142 buf[1] = NULLCHAR;
\r
7143 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7144 quoteNextChar = FALSE;
\r
7148 case '\r': /* Enter key */
\r
7149 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7150 if (consoleEcho) SaveInHistory(is->buf);
\r
7151 is->buf[is->count++] = '\n';
\r
7152 is->buf[is->count] = NULLCHAR;
\r
7153 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7154 if (consoleEcho) {
\r
7155 ConsoleOutput(is->buf, is->count, TRUE);
\r
7156 } else if (appData.localLineEditing) {
\r
7157 ConsoleOutput("\n", 1, TRUE);
\r
7160 case '\033': /* Escape key */
\r
7161 SetWindowText(hwnd, "");
\r
7162 cf.cbSize = sizeof(CHARFORMAT);
\r
7163 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7164 if (consoleEcho) {
\r
7165 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7167 cf.crTextColor = COLOR_ECHOOFF;
\r
7169 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7170 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7172 case '\t': /* Tab key */
\r
7173 if (GetKeyState(VK_SHIFT) < 0) {
\r
7175 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7178 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7179 if (buttonDesc[0].hwnd) {
\r
7180 SetFocus(buttonDesc[0].hwnd);
\r
7182 SetFocus(hwndMain);
\r
7186 case '\023': /* Ctrl+S */
\r
7187 sendNextChar = TRUE;
\r
7189 case '\021': /* Ctrl+Q */
\r
7190 quoteNextChar = TRUE;
\r
7200 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7201 p = PrevInHistory(buf);
\r
7203 SetWindowText(hwnd, p);
\r
7204 sel.cpMin = 999999;
\r
7205 sel.cpMax = 999999;
\r
7206 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7211 p = NextInHistory();
\r
7213 SetWindowText(hwnd, p);
\r
7214 sel.cpMin = 999999;
\r
7215 sel.cpMax = 999999;
\r
7216 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7222 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7226 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7230 case WM_MBUTTONDOWN:
\r
7231 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7232 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7234 case WM_RBUTTONUP:
\r
7235 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7236 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7237 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7241 hmenu = LoadMenu(hInst, "InputMenu");
\r
7242 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7243 if (sel.cpMin == sel.cpMax) {
\r
7244 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7245 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7247 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7248 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7250 pt.x = LOWORD(lParam);
\r
7251 pt.y = HIWORD(lParam);
\r
7252 MenuPopup(hwnd, pt, hmenu, -1);
\r
7256 switch (LOWORD(wParam)) {
\r
7258 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7260 case IDM_SelectAll:
\r
7262 sel.cpMax = -1; /*999999?*/
\r
7263 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7266 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7269 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7272 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7277 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7280 #define CO_MAX 100000
\r
7281 #define CO_TRIM 1000
\r
7284 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7286 static SnapData sd;
\r
7287 HWND hText, hInput;
\r
7289 static int sizeX, sizeY;
\r
7290 int newSizeX, newSizeY;
\r
7294 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7295 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7297 switch (message) {
\r
7299 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7301 ENLINK *pLink = (ENLINK*)lParam;
\r
7302 if (pLink->msg == WM_LBUTTONUP)
\r
7306 tr.chrg = pLink->chrg;
\r
7307 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7308 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7309 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7310 free(tr.lpstrText);
\r
7314 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7315 hwndConsole = hDlg;
\r
7317 consoleTextWindowProc = (WNDPROC)
\r
7318 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7319 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7320 consoleInputWindowProc = (WNDPROC)
\r
7321 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7322 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7323 Colorize(ColorNormal, TRUE);
\r
7324 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7325 ChangedConsoleFont();
\r
7326 GetClientRect(hDlg, &rect);
\r
7327 sizeX = rect.right;
\r
7328 sizeY = rect.bottom;
\r
7329 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7330 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7331 WINDOWPLACEMENT wp;
\r
7332 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7333 wp.length = sizeof(WINDOWPLACEMENT);
\r
7335 wp.showCmd = SW_SHOW;
\r
7336 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7337 wp.rcNormalPosition.left = wpConsole.x;
\r
7338 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7339 wp.rcNormalPosition.top = wpConsole.y;
\r
7340 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7341 SetWindowPlacement(hDlg, &wp);
\r
7344 // [HGM] Chessknight's change 2004-07-13
\r
7345 else { /* Determine Defaults */
\r
7346 WINDOWPLACEMENT wp;
\r
7347 wpConsole.x = wpMain.width + 1;
\r
7348 wpConsole.y = wpMain.y;
\r
7349 wpConsole.width = screenWidth - wpMain.width;
\r
7350 wpConsole.height = wpMain.height;
\r
7351 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7352 wp.length = sizeof(WINDOWPLACEMENT);
\r
7354 wp.showCmd = SW_SHOW;
\r
7355 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7356 wp.rcNormalPosition.left = wpConsole.x;
\r
7357 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7358 wp.rcNormalPosition.top = wpConsole.y;
\r
7359 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7360 SetWindowPlacement(hDlg, &wp);
\r
7363 // Allow hText to highlight URLs and send notifications on them
\r
7364 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7365 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7366 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7367 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7381 if (IsIconic(hDlg)) break;
\r
7382 newSizeX = LOWORD(lParam);
\r
7383 newSizeY = HIWORD(lParam);
\r
7384 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7385 RECT rectText, rectInput;
\r
7387 int newTextHeight, newTextWidth;
\r
7388 GetWindowRect(hText, &rectText);
\r
7389 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7390 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7391 if (newTextHeight < 0) {
\r
7392 newSizeY += -newTextHeight;
\r
7393 newTextHeight = 0;
\r
7395 SetWindowPos(hText, NULL, 0, 0,
\r
7396 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7397 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7398 pt.x = rectInput.left;
\r
7399 pt.y = rectInput.top + newSizeY - sizeY;
\r
7400 ScreenToClient(hDlg, &pt);
\r
7401 SetWindowPos(hInput, NULL,
\r
7402 pt.x, pt.y, /* needs client coords */
\r
7403 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7404 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7410 case WM_GETMINMAXINFO:
\r
7411 /* Prevent resizing window too small */
\r
7412 mmi = (MINMAXINFO *) lParam;
\r
7413 mmi->ptMinTrackSize.x = 100;
\r
7414 mmi->ptMinTrackSize.y = 100;
\r
7417 /* [AS] Snapping */
\r
7418 case WM_ENTERSIZEMOVE:
\r
7419 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7422 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7425 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7427 case WM_EXITSIZEMOVE:
\r
7428 UpdateICSWidth(hText);
\r
7429 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7432 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7440 if (hwndConsole) return;
\r
7441 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7442 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7447 ConsoleOutput(char* data, int length, int forceVisible)
\r
7452 char buf[CO_MAX+1];
\r
7455 static int delayLF = 0;
\r
7456 CHARRANGE savesel, sel;
\r
7458 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7466 while (length--) {
\r
7474 } else if (*p == '\007') {
\r
7475 MyPlaySound(&sounds[(int)SoundBell]);
\r
7482 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7483 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7484 /* Save current selection */
\r
7485 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7486 exlen = GetWindowTextLength(hText);
\r
7487 /* Find out whether current end of text is visible */
\r
7488 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7489 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7490 /* Trim existing text if it's too long */
\r
7491 if (exlen + (q - buf) > CO_MAX) {
\r
7492 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7495 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7496 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7498 savesel.cpMin -= trim;
\r
7499 savesel.cpMax -= trim;
\r
7500 if (exlen < 0) exlen = 0;
\r
7501 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7502 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7504 /* Append the new text */
\r
7505 sel.cpMin = exlen;
\r
7506 sel.cpMax = exlen;
\r
7507 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7508 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7509 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7510 if (forceVisible || exlen == 0 ||
\r
7511 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7512 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7513 /* Scroll to make new end of text visible if old end of text
\r
7514 was visible or new text is an echo of user typein */
\r
7515 sel.cpMin = 9999999;
\r
7516 sel.cpMax = 9999999;
\r
7517 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7518 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7519 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7520 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7522 if (savesel.cpMax == exlen || forceVisible) {
\r
7523 /* Move insert point to new end of text if it was at the old
\r
7524 end of text or if the new text is an echo of user typein */
\r
7525 sel.cpMin = 9999999;
\r
7526 sel.cpMax = 9999999;
\r
7527 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7529 /* Restore previous selection */
\r
7530 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7532 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7539 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7543 COLORREF oldFg, oldBg;
\r
7547 if(copyNumber > 1)
\r
7548 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7550 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7551 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7552 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7555 rect.right = x + squareSize;
\r
7557 rect.bottom = y + squareSize;
\r
7560 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7561 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7562 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7563 &rect, str, strlen(str), NULL);
\r
7565 (void) SetTextColor(hdc, oldFg);
\r
7566 (void) SetBkColor(hdc, oldBg);
\r
7567 (void) SelectObject(hdc, oldFont);
\r
7571 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7572 RECT *rect, char *color, char *flagFell)
\r
7576 COLORREF oldFg, oldBg;
\r
7579 if (twoBoards && partnerUp) return;
\r
7580 if (appData.clockMode) {
\r
7582 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7584 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7591 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7592 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7594 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7595 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7597 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7601 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7602 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7603 rect, str, strlen(str), NULL);
\r
7604 if(logoHeight > 0 && appData.clockMode) {
\r
7606 str += strlen(color)+2;
\r
7607 r.top = rect->top + logoHeight/2;
\r
7608 r.left = rect->left;
\r
7609 r.right = rect->right;
\r
7610 r.bottom = rect->bottom;
\r
7611 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7612 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7613 &r, str, strlen(str), NULL);
\r
7615 (void) SetTextColor(hdc, oldFg);
\r
7616 (void) SetBkColor(hdc, oldBg);
\r
7617 (void) SelectObject(hdc, oldFont);
\r
7622 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7628 if( count <= 0 ) {
\r
7629 if (appData.debugMode) {
\r
7630 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7633 return ERROR_INVALID_USER_BUFFER;
\r
7636 ResetEvent(ovl->hEvent);
\r
7637 ovl->Offset = ovl->OffsetHigh = 0;
\r
7638 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7642 err = GetLastError();
\r
7643 if (err == ERROR_IO_PENDING) {
\r
7644 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7648 err = GetLastError();
\r
7655 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7660 ResetEvent(ovl->hEvent);
\r
7661 ovl->Offset = ovl->OffsetHigh = 0;
\r
7662 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7666 err = GetLastError();
\r
7667 if (err == ERROR_IO_PENDING) {
\r
7668 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7672 err = GetLastError();
\r
7678 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7679 void CheckForInputBufferFull( InputSource * is )
\r
7681 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7682 /* Look for end of line */
\r
7683 char * p = is->buf;
\r
7685 while( p < is->next && *p != '\n' ) {
\r
7689 if( p >= is->next ) {
\r
7690 if (appData.debugMode) {
\r
7691 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7694 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7695 is->count = (DWORD) -1;
\r
7696 is->next = is->buf;
\r
7702 InputThread(LPVOID arg)
\r
7707 is = (InputSource *) arg;
\r
7708 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7709 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7710 while (is->hThread != NULL) {
\r
7711 is->error = DoReadFile(is->hFile, is->next,
\r
7712 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7713 &is->count, &ovl);
\r
7714 if (is->error == NO_ERROR) {
\r
7715 is->next += is->count;
\r
7717 if (is->error == ERROR_BROKEN_PIPE) {
\r
7718 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7721 is->count = (DWORD) -1;
\r
7722 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7727 CheckForInputBufferFull( is );
\r
7729 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7731 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7733 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7736 CloseHandle(ovl.hEvent);
\r
7737 CloseHandle(is->hFile);
\r
7739 if (appData.debugMode) {
\r
7740 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7747 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7749 NonOvlInputThread(LPVOID arg)
\r
7756 is = (InputSource *) arg;
\r
7757 while (is->hThread != NULL) {
\r
7758 is->error = ReadFile(is->hFile, is->next,
\r
7759 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7760 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7761 if (is->error == NO_ERROR) {
\r
7762 /* Change CRLF to LF */
\r
7763 if (is->next > is->buf) {
\r
7765 i = is->count + 1;
\r
7773 if (prev == '\r' && *p == '\n') {
\r
7785 if (is->error == ERROR_BROKEN_PIPE) {
\r
7786 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7789 is->count = (DWORD) -1;
\r
7793 CheckForInputBufferFull( is );
\r
7795 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7797 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7799 if (is->count < 0) break; /* Quit on error */
\r
7801 CloseHandle(is->hFile);
\r
7806 SocketInputThread(LPVOID arg)
\r
7810 is = (InputSource *) arg;
\r
7811 while (is->hThread != NULL) {
\r
7812 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7813 if ((int)is->count == SOCKET_ERROR) {
\r
7814 is->count = (DWORD) -1;
\r
7815 is->error = WSAGetLastError();
\r
7817 is->error = NO_ERROR;
\r
7818 is->next += is->count;
\r
7819 if (is->count == 0 && is->second == is) {
\r
7820 /* End of file on stderr; quit with no message */
\r
7824 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7826 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7828 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7834 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7838 is = (InputSource *) lParam;
\r
7839 if (is->lineByLine) {
\r
7840 /* Feed in lines one by one */
\r
7841 char *p = is->buf;
\r
7843 while (q < is->next) {
\r
7844 if (*q++ == '\n') {
\r
7845 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7850 /* Move any partial line to the start of the buffer */
\r
7852 while (p < is->next) {
\r
7857 if (is->error != NO_ERROR || is->count == 0) {
\r
7858 /* Notify backend of the error. Note: If there was a partial
\r
7859 line at the end, it is not flushed through. */
\r
7860 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7863 /* Feed in the whole chunk of input at once */
\r
7864 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7865 is->next = is->buf;
\r
7869 /*---------------------------------------------------------------------------*\
\r
7871 * Menu enables. Used when setting various modes.
\r
7873 \*---------------------------------------------------------------------------*/
\r
7881 GreyRevert(Boolean grey)
\r
7882 { // [HGM] vari: for retracting variations in local mode
\r
7883 HMENU hmenu = GetMenu(hwndMain);
\r
7884 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7885 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7889 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7891 while (enab->item > 0) {
\r
7892 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7897 Enables gnuEnables[] = {
\r
7898 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7899 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7900 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7901 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7902 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7903 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7904 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7905 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7906 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7907 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7908 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7909 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7910 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7912 // Needed to switch from ncp to GNU mode on Engine Load
\r
7913 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7914 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7915 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7916 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7917 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7918 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7919 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
7920 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7921 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
7922 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
7923 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7924 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7925 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7926 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7930 Enables icsEnables[] = {
\r
7931 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7932 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7933 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7934 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7935 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7936 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7937 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7938 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7939 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7940 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7941 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7942 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7943 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7944 { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },
\r
7945 { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },
\r
7946 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7947 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7948 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7949 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7950 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
7955 Enables zippyEnables[] = {
\r
7956 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7957 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7958 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7959 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7964 Enables ncpEnables[] = {
\r
7965 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7966 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7967 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7968 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7969 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7970 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7971 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7972 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7973 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7974 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7975 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7976 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7977 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7978 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7979 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7980 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7981 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7982 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7983 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7984 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7985 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7986 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
7990 Enables trainingOnEnables[] = {
\r
7991 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7992 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
7993 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7994 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7995 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7996 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7997 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7998 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7999 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
8003 Enables trainingOffEnables[] = {
\r
8004 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
8005 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
8006 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
8007 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
8008 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
8009 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
8010 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
8011 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8012 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
8016 /* These modify either ncpEnables or gnuEnables */
\r
8017 Enables cmailEnables[] = {
\r
8018 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
8019 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
8020 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
8021 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
8022 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
8023 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
8024 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
8028 Enables machineThinkingEnables[] = {
\r
8029 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8030 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
8031 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
8032 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8033 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
8034 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8035 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8036 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8037 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8038 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
8039 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8040 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8041 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8042 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8043 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
8044 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
8048 Enables userThinkingEnables[] = {
\r
8049 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8050 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
8051 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
8052 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8053 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
8054 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8055 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8056 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8057 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8058 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
8059 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
8060 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
8061 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
8062 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
8063 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
8064 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
8068 /*---------------------------------------------------------------------------*\
\r
8070 * Front-end interface functions exported by XBoard.
\r
8071 * Functions appear in same order as prototypes in frontend.h.
\r
8073 \*---------------------------------------------------------------------------*/
\r
8075 CheckMark(UINT item, int state)
\r
8077 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
8083 static UINT prevChecked = 0;
\r
8084 static int prevPausing = 0;
\r
8087 if (pausing != prevPausing) {
\r
8088 prevPausing = pausing;
\r
8089 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
8090 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
8091 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
8094 switch (gameMode) {
\r
8095 case BeginningOfGame:
\r
8096 if (appData.icsActive)
\r
8097 nowChecked = IDM_IcsClient;
\r
8098 else if (appData.noChessProgram)
\r
8099 nowChecked = IDM_EditGame;
\r
8101 nowChecked = IDM_MachineBlack;
\r
8103 case MachinePlaysBlack:
\r
8104 nowChecked = IDM_MachineBlack;
\r
8106 case MachinePlaysWhite:
\r
8107 nowChecked = IDM_MachineWhite;
\r
8109 case TwoMachinesPlay:
\r
8110 nowChecked = IDM_TwoMachines;
\r
8113 nowChecked = IDM_AnalysisMode;
\r
8116 nowChecked = IDM_AnalyzeFile;
\r
8119 nowChecked = IDM_EditGame;
\r
8121 case PlayFromGameFile:
\r
8122 nowChecked = IDM_LoadGame;
\r
8124 case EditPosition:
\r
8125 nowChecked = IDM_EditPosition;
\r
8128 nowChecked = IDM_Training;
\r
8130 case IcsPlayingWhite:
\r
8131 case IcsPlayingBlack:
\r
8132 case IcsObserving:
\r
8134 nowChecked = IDM_IcsClient;
\r
8141 CheckMark(prevChecked, MF_UNCHECKED);
\r
8142 CheckMark(nowChecked, MF_CHECKED);
\r
8143 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8145 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8146 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8147 MF_BYCOMMAND|MF_ENABLED);
\r
8149 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8150 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8153 prevChecked = nowChecked;
\r
8155 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8156 if (appData.icsActive) {
\r
8157 if (appData.icsEngineAnalyze) {
\r
8158 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8160 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8163 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8169 HMENU hmenu = GetMenu(hwndMain);
\r
8170 SetMenuEnables(hmenu, icsEnables);
\r
8171 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8172 MF_BYCOMMAND|MF_ENABLED);
\r
8174 if (appData.zippyPlay) {
\r
8175 SetMenuEnables(hmenu, zippyEnables);
\r
8176 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8177 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8178 MF_BYCOMMAND|MF_ENABLED);
\r
8186 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8192 HMENU hmenu = GetMenu(hwndMain);
\r
8193 SetMenuEnables(hmenu, ncpEnables);
\r
8194 DrawMenuBar(hwndMain);
\r
8200 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8204 SetTrainingModeOn()
\r
8207 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8208 for (i = 0; i < N_BUTTONS; i++) {
\r
8209 if (buttonDesc[i].hwnd != NULL)
\r
8210 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8215 VOID SetTrainingModeOff()
\r
8218 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8219 for (i = 0; i < N_BUTTONS; i++) {
\r
8220 if (buttonDesc[i].hwnd != NULL)
\r
8221 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8227 SetUserThinkingEnables()
\r
8229 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8233 SetMachineThinkingEnables()
\r
8235 HMENU hMenu = GetMenu(hwndMain);
\r
8236 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8238 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8240 if (gameMode == MachinePlaysBlack) {
\r
8241 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8242 } else if (gameMode == MachinePlaysWhite) {
\r
8243 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8244 } else if (gameMode == TwoMachinesPlay) {
\r
8245 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8251 DisplayTitle(char *str)
\r
8253 char title[MSG_SIZ], *host;
\r
8254 if (str[0] != NULLCHAR) {
\r
8255 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8256 } else if (appData.icsActive) {
\r
8257 if (appData.icsCommPort[0] != NULLCHAR)
\r
8260 host = appData.icsHost;
\r
8261 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8262 } else if (appData.noChessProgram) {
\r
8263 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8265 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8266 strcat(title, ": ");
\r
8267 strcat(title, first.tidy);
\r
8269 SetWindowText(hwndMain, title);
\r
8274 DisplayMessage(char *str1, char *str2)
\r
8278 int remain = MESSAGE_TEXT_MAX - 1;
\r
8281 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8282 messageText[0] = NULLCHAR;
\r
8284 len = strlen(str1);
\r
8285 if (len > remain) len = remain;
\r
8286 strncpy(messageText, str1, len);
\r
8287 messageText[len] = NULLCHAR;
\r
8290 if (*str2 && remain >= 2) {
\r
8292 strcat(messageText, " ");
\r
8295 len = strlen(str2);
\r
8296 if (len > remain) len = remain;
\r
8297 strncat(messageText, str2, len);
\r
8299 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8300 safeStrCpy(lastMsg, messageText, MSG_SIZ);
\r
8302 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8306 hdc = GetDC(hwndMain);
\r
8307 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8308 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8309 &messageRect, messageText, strlen(messageText), NULL);
\r
8310 (void) SelectObject(hdc, oldFont);
\r
8311 (void) ReleaseDC(hwndMain, hdc);
\r
8315 DisplayError(char *str, int error)
\r
8317 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8321 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8323 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8324 NULL, error, LANG_NEUTRAL,
\r
8325 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8327 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8329 ErrorMap *em = errmap;
\r
8330 while (em->err != 0 && em->err != error) em++;
\r
8331 if (em->err != 0) {
\r
8332 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8334 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8339 ErrorPopUp(_("Error"), buf);
\r
8344 DisplayMoveError(char *str)
\r
8346 fromX = fromY = -1;
\r
8347 ClearHighlights();
\r
8348 DrawPosition(FALSE, NULL);
\r
8349 if (appData.popupMoveErrors) {
\r
8350 ErrorPopUp(_("Error"), str);
\r
8352 DisplayMessage(str, "");
\r
8353 moveErrorMessageUp = TRUE;
\r
8358 DisplayFatalError(char *str, int error, int exitStatus)
\r
8360 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8362 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8365 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8366 NULL, error, LANG_NEUTRAL,
\r
8367 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8369 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8371 ErrorMap *em = errmap;
\r
8372 while (em->err != 0 && em->err != error) em++;
\r
8373 if (em->err != 0) {
\r
8374 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8376 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8381 if (appData.debugMode) {
\r
8382 fprintf(debugFP, "%s: %s\n", label, str);
\r
8384 if (appData.popupExitMessage) {
\r
8385 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8386 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8388 ExitEvent(exitStatus);
\r
8393 DisplayInformation(char *str)
\r
8395 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8400 DisplayNote(char *str)
\r
8402 ErrorPopUp(_("Note"), str);
\r
8407 char *title, *question, *replyPrefix;
\r
8412 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8414 static QuestionParams *qp;
\r
8415 char reply[MSG_SIZ];
\r
8418 switch (message) {
\r
8419 case WM_INITDIALOG:
\r
8420 qp = (QuestionParams *) lParam;
\r
8421 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8422 Translate(hDlg, DLG_Question);
\r
8423 SetWindowText(hDlg, qp->title);
\r
8424 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8425 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8429 switch (LOWORD(wParam)) {
\r
8431 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8432 if (*reply) strcat(reply, " ");
\r
8433 len = strlen(reply);
\r
8434 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8435 strcat(reply, "\n");
\r
8436 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8437 EndDialog(hDlg, TRUE);
\r
8438 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8441 EndDialog(hDlg, FALSE);
\r
8452 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8454 QuestionParams qp;
\r
8458 qp.question = question;
\r
8459 qp.replyPrefix = replyPrefix;
\r
8461 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8462 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8463 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8464 FreeProcInstance(lpProc);
\r
8467 /* [AS] Pick FRC position */
\r
8468 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8470 static int * lpIndexFRC;
\r
8476 case WM_INITDIALOG:
\r
8477 lpIndexFRC = (int *) lParam;
\r
8479 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8480 Translate(hDlg, DLG_NewGameFRC);
\r
8482 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8483 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8484 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8485 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8490 switch( LOWORD(wParam) ) {
\r
8492 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8493 EndDialog( hDlg, 0 );
\r
8494 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8497 EndDialog( hDlg, 1 );
\r
8499 case IDC_NFG_Edit:
\r
8500 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8501 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8503 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8506 case IDC_NFG_Random:
\r
8507 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8508 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8521 int index = appData.defaultFrcPosition;
\r
8522 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8524 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8526 if( result == 0 ) {
\r
8527 appData.defaultFrcPosition = index;
\r
8533 /* [AS] Game list options. Refactored by HGM */
\r
8535 HWND gameListOptionsDialog;
\r
8537 // low-level front-end: clear text edit / list widget
\r
8541 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8544 // low-level front-end: clear text edit / list widget
\r
8546 GLT_DeSelectList()
\r
8548 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8551 // low-level front-end: append line to text edit / list widget
\r
8553 GLT_AddToList( char *name )
\r
8556 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8560 // low-level front-end: get line from text edit / list widget
\r
8562 GLT_GetFromList( int index, char *name )
\r
8565 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8571 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8573 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8574 int idx2 = idx1 + delta;
\r
8575 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8577 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8580 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8581 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8582 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8583 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8587 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8591 case WM_INITDIALOG:
\r
8592 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8594 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8595 Translate(hDlg, DLG_GameListOptions);
\r
8597 /* Initialize list */
\r
8598 GLT_TagsToList( lpUserGLT );
\r
8600 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8605 switch( LOWORD(wParam) ) {
\r
8608 EndDialog( hDlg, 0 );
\r
8611 EndDialog( hDlg, 1 );
\r
8614 case IDC_GLT_Default:
\r
8615 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8618 case IDC_GLT_Restore:
\r
8619 GLT_TagsToList( appData.gameListTags );
\r
8623 GLT_MoveSelection( hDlg, -1 );
\r
8626 case IDC_GLT_Down:
\r
8627 GLT_MoveSelection( hDlg, +1 );
\r
8637 int GameListOptions()
\r
8640 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8642 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8644 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8646 if( result == 0 ) {
\r
8647 /* [AS] Memory leak here! */
\r
8648 appData.gameListTags = strdup( lpUserGLT );
\r
8655 DisplayIcsInteractionTitle(char *str)
\r
8657 char consoleTitle[MSG_SIZ];
\r
8659 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8660 SetWindowText(hwndConsole, consoleTitle);
\r
8662 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
8663 char buf[MSG_SIZ], *p = buf, *q;
\r
8664 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
8666 q = strchr(p, ';');
\r
8668 if(*p) ChatPopUp(p);
\r
8672 SetActiveWindow(hwndMain);
\r
8676 DrawPosition(int fullRedraw, Board board)
\r
8678 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8681 void NotifyFrontendLogin()
\r
8684 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8690 fromX = fromY = -1;
\r
8691 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8692 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8693 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8694 dragInfo.lastpos = dragInfo.pos;
\r
8695 dragInfo.start.x = dragInfo.start.y = -1;
\r
8696 dragInfo.from = dragInfo.start;
\r
8698 DrawPosition(TRUE, NULL);
\r
8705 CommentPopUp(char *title, char *str)
\r
8707 HWND hwnd = GetActiveWindow();
\r
8708 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8710 SetActiveWindow(hwnd);
\r
8714 CommentPopDown(void)
\r
8716 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8717 if (commentDialog) {
\r
8718 ShowWindow(commentDialog, SW_HIDE);
\r
8720 commentUp = FALSE;
\r
8724 EditCommentPopUp(int index, char *title, char *str)
\r
8726 EitherCommentPopUp(index, title, str, TRUE);
\r
8733 MyPlaySound(&sounds[(int)SoundRoar]);
\r
8740 MyPlaySound(&sounds[(int)SoundMove]);
\r
8743 VOID PlayIcsWinSound()
\r
8745 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8748 VOID PlayIcsLossSound()
\r
8750 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8753 VOID PlayIcsDrawSound()
\r
8755 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8758 VOID PlayIcsUnfinishedSound()
\r
8760 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8766 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8772 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8780 consoleEcho = TRUE;
\r
8781 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8782 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8783 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8792 consoleEcho = FALSE;
\r
8793 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8794 /* This works OK: set text and background both to the same color */
\r
8796 cf.crTextColor = COLOR_ECHOOFF;
\r
8797 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8798 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8801 /* No Raw()...? */
\r
8803 void Colorize(ColorClass cc, int continuation)
\r
8805 currentColorClass = cc;
\r
8806 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8807 consoleCF.crTextColor = textAttribs[cc].color;
\r
8808 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8809 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8815 static char buf[MSG_SIZ];
\r
8816 DWORD bufsiz = MSG_SIZ;
\r
8818 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8819 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8821 if (!GetUserName(buf, &bufsiz)) {
\r
8822 /*DisplayError("Error getting user name", GetLastError());*/
\r
8823 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8831 static char buf[MSG_SIZ];
\r
8832 DWORD bufsiz = MSG_SIZ;
\r
8834 if (!GetComputerName(buf, &bufsiz)) {
\r
8835 /*DisplayError("Error getting host name", GetLastError());*/
\r
8836 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8843 ClockTimerRunning()
\r
8845 return clockTimerEvent != 0;
\r
8851 if (clockTimerEvent == 0) return FALSE;
\r
8852 KillTimer(hwndMain, clockTimerEvent);
\r
8853 clockTimerEvent = 0;
\r
8858 StartClockTimer(long millisec)
\r
8860 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8861 (UINT) millisec, NULL);
\r
8865 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8868 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8870 if(appData.noGUI) return;
\r
8871 hdc = GetDC(hwndMain);
\r
8872 if (!IsIconic(hwndMain)) {
\r
8873 DisplayAClock(hdc, timeRemaining, highlight,
\r
8874 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8876 if (highlight && iconCurrent == iconBlack) {
\r
8877 iconCurrent = iconWhite;
\r
8878 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8879 if (IsIconic(hwndMain)) {
\r
8880 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8883 (void) ReleaseDC(hwndMain, hdc);
\r
8885 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8889 DisplayBlackClock(long timeRemaining, int highlight)
\r
8892 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8895 if(appData.noGUI) return;
\r
8896 hdc = GetDC(hwndMain);
\r
8897 if (!IsIconic(hwndMain)) {
\r
8898 DisplayAClock(hdc, timeRemaining, highlight,
\r
8899 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8901 if (highlight && iconCurrent == iconWhite) {
\r
8902 iconCurrent = iconBlack;
\r
8903 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8904 if (IsIconic(hwndMain)) {
\r
8905 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8908 (void) ReleaseDC(hwndMain, hdc);
\r
8910 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8915 LoadGameTimerRunning()
\r
8917 return loadGameTimerEvent != 0;
\r
8921 StopLoadGameTimer()
\r
8923 if (loadGameTimerEvent == 0) return FALSE;
\r
8924 KillTimer(hwndMain, loadGameTimerEvent);
\r
8925 loadGameTimerEvent = 0;
\r
8930 StartLoadGameTimer(long millisec)
\r
8932 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8933 (UINT) millisec, NULL);
\r
8941 char fileTitle[MSG_SIZ];
\r
8943 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8944 f = OpenFileDialog(hwndMain, "a", defName,
\r
8945 appData.oldSaveStyle ? "gam" : "pgn",
\r
8947 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8949 SaveGame(f, 0, "");
\r
8956 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8958 if (delayedTimerEvent != 0) {
\r
8959 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8960 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8962 KillTimer(hwndMain, delayedTimerEvent);
\r
8963 delayedTimerEvent = 0;
\r
8964 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8965 delayedTimerCallback();
\r
8967 delayedTimerCallback = cb;
\r
8968 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8969 (UINT) millisec, NULL);
\r
8972 DelayedEventCallback
\r
8975 if (delayedTimerEvent) {
\r
8976 return delayedTimerCallback;
\r
8983 CancelDelayedEvent()
\r
8985 if (delayedTimerEvent) {
\r
8986 KillTimer(hwndMain, delayedTimerEvent);
\r
8987 delayedTimerEvent = 0;
\r
8991 DWORD GetWin32Priority(int nice)
\r
8992 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8994 REALTIME_PRIORITY_CLASS 0x00000100
\r
8995 HIGH_PRIORITY_CLASS 0x00000080
\r
8996 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8997 NORMAL_PRIORITY_CLASS 0x00000020
\r
8998 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8999 IDLE_PRIORITY_CLASS 0x00000040
\r
9001 if (nice < -15) return 0x00000080;
\r
9002 if (nice < 0) return 0x00008000;
\r
9003 if (nice == 0) return 0x00000020;
\r
9004 if (nice < 15) return 0x00004000;
\r
9005 return 0x00000040;
\r
9008 void RunCommand(char *cmdLine)
\r
9010 /* Now create the child process. */
\r
9011 STARTUPINFO siStartInfo;
\r
9012 PROCESS_INFORMATION piProcInfo;
\r
9014 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9015 siStartInfo.lpReserved = NULL;
\r
9016 siStartInfo.lpDesktop = NULL;
\r
9017 siStartInfo.lpTitle = NULL;
\r
9018 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9019 siStartInfo.cbReserved2 = 0;
\r
9020 siStartInfo.lpReserved2 = NULL;
\r
9021 siStartInfo.hStdInput = NULL;
\r
9022 siStartInfo.hStdOutput = NULL;
\r
9023 siStartInfo.hStdError = NULL;
\r
9025 CreateProcess(NULL,
\r
9026 cmdLine, /* command line */
\r
9027 NULL, /* process security attributes */
\r
9028 NULL, /* primary thread security attrs */
\r
9029 TRUE, /* handles are inherited */
\r
9030 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9031 NULL, /* use parent's environment */
\r
9033 &siStartInfo, /* STARTUPINFO pointer */
\r
9034 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9036 CloseHandle(piProcInfo.hThread);
\r
9039 /* Start a child process running the given program.
\r
9040 The process's standard output can be read from "from", and its
\r
9041 standard input can be written to "to".
\r
9042 Exit with fatal error if anything goes wrong.
\r
9043 Returns an opaque pointer that can be used to destroy the process
\r
9047 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
9049 #define BUFSIZE 4096
\r
9051 HANDLE hChildStdinRd, hChildStdinWr,
\r
9052 hChildStdoutRd, hChildStdoutWr;
\r
9053 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
9054 SECURITY_ATTRIBUTES saAttr;
\r
9056 PROCESS_INFORMATION piProcInfo;
\r
9057 STARTUPINFO siStartInfo;
\r
9059 char buf[MSG_SIZ];
\r
9062 if (appData.debugMode) {
\r
9063 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
9068 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
9069 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
9070 saAttr.bInheritHandle = TRUE;
\r
9071 saAttr.lpSecurityDescriptor = NULL;
\r
9074 * The steps for redirecting child's STDOUT:
\r
9075 * 1. Create anonymous pipe to be STDOUT for child.
\r
9076 * 2. Create a noninheritable duplicate of read handle,
\r
9077 * and close the inheritable read handle.
\r
9080 /* Create a pipe for the child's STDOUT. */
\r
9081 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
9082 return GetLastError();
\r
9085 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
9086 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
9087 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
9088 FALSE, /* not inherited */
\r
9089 DUPLICATE_SAME_ACCESS);
\r
9091 return GetLastError();
\r
9093 CloseHandle(hChildStdoutRd);
\r
9096 * The steps for redirecting child's STDIN:
\r
9097 * 1. Create anonymous pipe to be STDIN for child.
\r
9098 * 2. Create a noninheritable duplicate of write handle,
\r
9099 * and close the inheritable write handle.
\r
9102 /* Create a pipe for the child's STDIN. */
\r
9103 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
9104 return GetLastError();
\r
9107 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
9108 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
9109 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
9110 FALSE, /* not inherited */
\r
9111 DUPLICATE_SAME_ACCESS);
\r
9113 return GetLastError();
\r
9115 CloseHandle(hChildStdinWr);
\r
9117 /* Arrange to (1) look in dir for the child .exe file, and
\r
9118 * (2) have dir be the child's working directory. Interpret
\r
9119 * dir relative to the directory WinBoard loaded from. */
\r
9120 GetCurrentDirectory(MSG_SIZ, buf);
\r
9121 SetCurrentDirectory(installDir);
\r
9122 SetCurrentDirectory(dir);
\r
9124 /* Now create the child process. */
\r
9126 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9127 siStartInfo.lpReserved = NULL;
\r
9128 siStartInfo.lpDesktop = NULL;
\r
9129 siStartInfo.lpTitle = NULL;
\r
9130 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9131 siStartInfo.cbReserved2 = 0;
\r
9132 siStartInfo.lpReserved2 = NULL;
\r
9133 siStartInfo.hStdInput = hChildStdinRd;
\r
9134 siStartInfo.hStdOutput = hChildStdoutWr;
\r
9135 siStartInfo.hStdError = hChildStdoutWr;
\r
9137 fSuccess = CreateProcess(NULL,
\r
9138 cmdLine, /* command line */
\r
9139 NULL, /* process security attributes */
\r
9140 NULL, /* primary thread security attrs */
\r
9141 TRUE, /* handles are inherited */
\r
9142 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9143 NULL, /* use parent's environment */
\r
9145 &siStartInfo, /* STARTUPINFO pointer */
\r
9146 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9148 err = GetLastError();
\r
9149 SetCurrentDirectory(buf); /* return to prev directory */
\r
9154 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
9155 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
9156 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
9159 /* Close the handles we don't need in the parent */
\r
9160 CloseHandle(piProcInfo.hThread);
\r
9161 CloseHandle(hChildStdinRd);
\r
9162 CloseHandle(hChildStdoutWr);
\r
9164 /* Prepare return value */
\r
9165 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9166 cp->kind = CPReal;
\r
9167 cp->hProcess = piProcInfo.hProcess;
\r
9168 cp->pid = piProcInfo.dwProcessId;
\r
9169 cp->hFrom = hChildStdoutRdDup;
\r
9170 cp->hTo = hChildStdinWrDup;
\r
9172 *pr = (void *) cp;
\r
9174 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
9175 2000 where engines sometimes don't see the initial command(s)
\r
9176 from WinBoard and hang. I don't understand how that can happen,
\r
9177 but the Sleep is harmless, so I've put it in. Others have also
\r
9178 reported what may be the same problem, so hopefully this will fix
\r
9179 it for them too. */
\r
9187 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
9189 ChildProc *cp; int result;
\r
9191 cp = (ChildProc *) pr;
\r
9192 if (cp == NULL) return;
\r
9194 switch (cp->kind) {
\r
9196 /* TerminateProcess is considered harmful, so... */
\r
9197 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9198 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9199 /* The following doesn't work because the chess program
\r
9200 doesn't "have the same console" as WinBoard. Maybe
\r
9201 we could arrange for this even though neither WinBoard
\r
9202 nor the chess program uses a console for stdio? */
\r
9203 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9205 /* [AS] Special termination modes for misbehaving programs... */
\r
9206 if( signal == 9 ) {
\r
9207 result = TerminateProcess( cp->hProcess, 0 );
\r
9209 if ( appData.debugMode) {
\r
9210 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9213 else if( signal == 10 ) {
\r
9214 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
9216 if( dw != WAIT_OBJECT_0 ) {
\r
9217 result = TerminateProcess( cp->hProcess, 0 );
\r
9219 if ( appData.debugMode) {
\r
9220 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9226 CloseHandle(cp->hProcess);
\r
9230 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9234 closesocket(cp->sock);
\r
9239 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9240 closesocket(cp->sock);
\r
9241 closesocket(cp->sock2);
\r
9249 InterruptChildProcess(ProcRef pr)
\r
9253 cp = (ChildProc *) pr;
\r
9254 if (cp == NULL) return;
\r
9255 switch (cp->kind) {
\r
9257 /* The following doesn't work because the chess program
\r
9258 doesn't "have the same console" as WinBoard. Maybe
\r
9259 we could arrange for this even though neither WinBoard
\r
9260 nor the chess program uses a console for stdio */
\r
9261 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9266 /* Can't interrupt */
\r
9270 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9277 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9279 char cmdLine[MSG_SIZ];
\r
9281 if (port[0] == NULLCHAR) {
\r
9282 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9284 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9286 return StartChildProcess(cmdLine, "", pr);
\r
9290 /* Code to open TCP sockets */
\r
9293 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9299 struct sockaddr_in sa, mysa;
\r
9300 struct hostent FAR *hp;
\r
9301 unsigned short uport;
\r
9302 WORD wVersionRequested;
\r
9305 /* Initialize socket DLL */
\r
9306 wVersionRequested = MAKEWORD(1, 1);
\r
9307 err = WSAStartup(wVersionRequested, &wsaData);
\r
9308 if (err != 0) return err;
\r
9311 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9312 err = WSAGetLastError();
\r
9317 /* Bind local address using (mostly) don't-care values.
\r
9319 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9320 mysa.sin_family = AF_INET;
\r
9321 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9322 uport = (unsigned short) 0;
\r
9323 mysa.sin_port = htons(uport);
\r
9324 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9325 == SOCKET_ERROR) {
\r
9326 err = WSAGetLastError();
\r
9331 /* Resolve remote host name */
\r
9332 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9333 if (!(hp = gethostbyname(host))) {
\r
9334 unsigned int b0, b1, b2, b3;
\r
9336 err = WSAGetLastError();
\r
9338 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9339 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9340 hp->h_addrtype = AF_INET;
\r
9342 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9343 hp->h_addr_list[0] = (char *) malloc(4);
\r
9344 hp->h_addr_list[0][0] = (char) b0;
\r
9345 hp->h_addr_list[0][1] = (char) b1;
\r
9346 hp->h_addr_list[0][2] = (char) b2;
\r
9347 hp->h_addr_list[0][3] = (char) b3;
\r
9353 sa.sin_family = hp->h_addrtype;
\r
9354 uport = (unsigned short) atoi(port);
\r
9355 sa.sin_port = htons(uport);
\r
9356 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9358 /* Make connection */
\r
9359 if (connect(s, (struct sockaddr *) &sa,
\r
9360 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9361 err = WSAGetLastError();
\r
9366 /* Prepare return value */
\r
9367 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9368 cp->kind = CPSock;
\r
9370 *pr = (ProcRef *) cp;
\r
9376 OpenCommPort(char *name, ProcRef *pr)
\r
9381 char fullname[MSG_SIZ];
\r
9383 if (*name != '\\')
\r
9384 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9386 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9388 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9389 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9390 if (h == (HANDLE) -1) {
\r
9391 return GetLastError();
\r
9395 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9397 /* Accumulate characters until a 100ms pause, then parse */
\r
9398 ct.ReadIntervalTimeout = 100;
\r
9399 ct.ReadTotalTimeoutMultiplier = 0;
\r
9400 ct.ReadTotalTimeoutConstant = 0;
\r
9401 ct.WriteTotalTimeoutMultiplier = 0;
\r
9402 ct.WriteTotalTimeoutConstant = 0;
\r
9403 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9405 /* Prepare return value */
\r
9406 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9407 cp->kind = CPComm;
\r
9410 *pr = (ProcRef *) cp;
\r
9416 OpenLoopback(ProcRef *pr)
\r
9418 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9424 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9429 struct sockaddr_in sa, mysa;
\r
9430 struct hostent FAR *hp;
\r
9431 unsigned short uport;
\r
9432 WORD wVersionRequested;
\r
9435 char stderrPortStr[MSG_SIZ];
\r
9437 /* Initialize socket DLL */
\r
9438 wVersionRequested = MAKEWORD(1, 1);
\r
9439 err = WSAStartup(wVersionRequested, &wsaData);
\r
9440 if (err != 0) return err;
\r
9442 /* Resolve remote host name */
\r
9443 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9444 if (!(hp = gethostbyname(host))) {
\r
9445 unsigned int b0, b1, b2, b3;
\r
9447 err = WSAGetLastError();
\r
9449 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9450 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9451 hp->h_addrtype = AF_INET;
\r
9453 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9454 hp->h_addr_list[0] = (char *) malloc(4);
\r
9455 hp->h_addr_list[0][0] = (char) b0;
\r
9456 hp->h_addr_list[0][1] = (char) b1;
\r
9457 hp->h_addr_list[0][2] = (char) b2;
\r
9458 hp->h_addr_list[0][3] = (char) b3;
\r
9464 sa.sin_family = hp->h_addrtype;
\r
9465 uport = (unsigned short) 514;
\r
9466 sa.sin_port = htons(uport);
\r
9467 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9469 /* Bind local socket to unused "privileged" port address
\r
9471 s = INVALID_SOCKET;
\r
9472 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9473 mysa.sin_family = AF_INET;
\r
9474 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9475 for (fromPort = 1023;; fromPort--) {
\r
9476 if (fromPort < 0) {
\r
9478 return WSAEADDRINUSE;
\r
9480 if (s == INVALID_SOCKET) {
\r
9481 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9482 err = WSAGetLastError();
\r
9487 uport = (unsigned short) fromPort;
\r
9488 mysa.sin_port = htons(uport);
\r
9489 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9490 == SOCKET_ERROR) {
\r
9491 err = WSAGetLastError();
\r
9492 if (err == WSAEADDRINUSE) continue;
\r
9496 if (connect(s, (struct sockaddr *) &sa,
\r
9497 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9498 err = WSAGetLastError();
\r
9499 if (err == WSAEADDRINUSE) {
\r
9510 /* Bind stderr local socket to unused "privileged" port address
\r
9512 s2 = INVALID_SOCKET;
\r
9513 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9514 mysa.sin_family = AF_INET;
\r
9515 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9516 for (fromPort = 1023;; fromPort--) {
\r
9517 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9518 if (fromPort < 0) {
\r
9519 (void) closesocket(s);
\r
9521 return WSAEADDRINUSE;
\r
9523 if (s2 == INVALID_SOCKET) {
\r
9524 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9525 err = WSAGetLastError();
\r
9531 uport = (unsigned short) fromPort;
\r
9532 mysa.sin_port = htons(uport);
\r
9533 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9534 == SOCKET_ERROR) {
\r
9535 err = WSAGetLastError();
\r
9536 if (err == WSAEADDRINUSE) continue;
\r
9537 (void) closesocket(s);
\r
9541 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9542 err = WSAGetLastError();
\r
9543 if (err == WSAEADDRINUSE) {
\r
9545 s2 = INVALID_SOCKET;
\r
9548 (void) closesocket(s);
\r
9549 (void) closesocket(s2);
\r
9555 prevStderrPort = fromPort; // remember port used
\r
9556 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9558 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9559 err = WSAGetLastError();
\r
9560 (void) closesocket(s);
\r
9561 (void) closesocket(s2);
\r
9566 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9567 err = WSAGetLastError();
\r
9568 (void) closesocket(s);
\r
9569 (void) closesocket(s2);
\r
9573 if (*user == NULLCHAR) user = UserName();
\r
9574 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9575 err = WSAGetLastError();
\r
9576 (void) closesocket(s);
\r
9577 (void) closesocket(s2);
\r
9581 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9582 err = WSAGetLastError();
\r
9583 (void) closesocket(s);
\r
9584 (void) closesocket(s2);
\r
9589 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9590 err = WSAGetLastError();
\r
9591 (void) closesocket(s);
\r
9592 (void) closesocket(s2);
\r
9596 (void) closesocket(s2); /* Stop listening */
\r
9598 /* Prepare return value */
\r
9599 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9600 cp->kind = CPRcmd;
\r
9603 *pr = (ProcRef *) cp;
\r
9610 AddInputSource(ProcRef pr, int lineByLine,
\r
9611 InputCallback func, VOIDSTAR closure)
\r
9613 InputSource *is, *is2 = NULL;
\r
9614 ChildProc *cp = (ChildProc *) pr;
\r
9616 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9617 is->lineByLine = lineByLine;
\r
9619 is->closure = closure;
\r
9620 is->second = NULL;
\r
9621 is->next = is->buf;
\r
9622 if (pr == NoProc) {
\r
9623 is->kind = CPReal;
\r
9624 consoleInputSource = is;
\r
9626 is->kind = cp->kind;
\r
9628 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9629 we create all threads suspended so that the is->hThread variable can be
\r
9630 safely assigned, then let the threads start with ResumeThread.
\r
9632 switch (cp->kind) {
\r
9634 is->hFile = cp->hFrom;
\r
9635 cp->hFrom = NULL; /* now owned by InputThread */
\r
9637 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9638 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9642 is->hFile = cp->hFrom;
\r
9643 cp->hFrom = NULL; /* now owned by InputThread */
\r
9645 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9646 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9650 is->sock = cp->sock;
\r
9652 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9653 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9657 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9659 is->sock = cp->sock;
\r
9661 is2->sock = cp->sock2;
\r
9662 is2->second = is2;
\r
9664 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9665 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9667 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9668 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9672 if( is->hThread != NULL ) {
\r
9673 ResumeThread( is->hThread );
\r
9676 if( is2 != NULL && is2->hThread != NULL ) {
\r
9677 ResumeThread( is2->hThread );
\r
9681 return (InputSourceRef) is;
\r
9685 RemoveInputSource(InputSourceRef isr)
\r
9689 is = (InputSource *) isr;
\r
9690 is->hThread = NULL; /* tell thread to stop */
\r
9691 CloseHandle(is->hThread);
\r
9692 if (is->second != NULL) {
\r
9693 is->second->hThread = NULL;
\r
9694 CloseHandle(is->second->hThread);
\r
9698 int no_wrap(char *message, int count)
\r
9700 ConsoleOutput(message, count, FALSE);
\r
9705 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9708 int outCount = SOCKET_ERROR;
\r
9709 ChildProc *cp = (ChildProc *) pr;
\r
9710 static OVERLAPPED ovl;
\r
9711 static int line = 0;
\r
9715 if (appData.noJoin || !appData.useInternalWrap)
\r
9716 return no_wrap(message, count);
\r
9719 int width = get_term_width();
\r
9720 int len = wrap(NULL, message, count, width, &line);
\r
9721 char *msg = malloc(len);
\r
9725 return no_wrap(message, count);
\r
9728 dbgchk = wrap(msg, message, count, width, &line);
\r
9729 if (dbgchk != len && appData.debugMode)
\r
9730 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9731 ConsoleOutput(msg, len, FALSE);
\r
9738 if (ovl.hEvent == NULL) {
\r
9739 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9741 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9743 switch (cp->kind) {
\r
9746 outCount = send(cp->sock, message, count, 0);
\r
9747 if (outCount == SOCKET_ERROR) {
\r
9748 *outError = WSAGetLastError();
\r
9750 *outError = NO_ERROR;
\r
9755 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9756 &dOutCount, NULL)) {
\r
9757 *outError = NO_ERROR;
\r
9758 outCount = (int) dOutCount;
\r
9760 *outError = GetLastError();
\r
9765 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9766 &dOutCount, &ovl);
\r
9767 if (*outError == NO_ERROR) {
\r
9768 outCount = (int) dOutCount;
\r
9778 if(n != 0) Sleep(n);
\r
9782 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9785 /* Ignore delay, not implemented for WinBoard */
\r
9786 return OutputToProcess(pr, message, count, outError);
\r
9791 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9792 char *buf, int count, int error)
\r
9794 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9797 /* see wgamelist.c for Game List functions */
\r
9798 /* see wedittags.c for Edit Tags functions */
\r
9805 char buf[MSG_SIZ];
\r
9808 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9809 f = fopen(buf, "r");
\r
9811 ProcessICSInitScript(f);
\r
9821 StartAnalysisClock()
\r
9823 if (analysisTimerEvent) return;
\r
9824 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9825 (UINT) 2000, NULL);
\r
9829 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9831 highlightInfo.sq[0].x = fromX;
\r
9832 highlightInfo.sq[0].y = fromY;
\r
9833 highlightInfo.sq[1].x = toX;
\r
9834 highlightInfo.sq[1].y = toY;
\r
9840 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9841 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9845 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9847 premoveHighlightInfo.sq[0].x = fromX;
\r
9848 premoveHighlightInfo.sq[0].y = fromY;
\r
9849 premoveHighlightInfo.sq[1].x = toX;
\r
9850 premoveHighlightInfo.sq[1].y = toY;
\r
9854 ClearPremoveHighlights()
\r
9856 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9857 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9861 ShutDownFrontEnd()
\r
9863 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9864 DeleteClipboardTempFiles();
\r
9870 if (IsIconic(hwndMain))
\r
9871 ShowWindow(hwndMain, SW_RESTORE);
\r
9873 SetActiveWindow(hwndMain);
\r
9877 * Prototypes for animation support routines
\r
9879 static void ScreenSquare(int column, int row, POINT * pt);
\r
9880 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9881 POINT frames[], int * nFrames);
\r
9887 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9888 { // [HGM] atomic: animate blast wave
\r
9891 explodeInfo.fromX = fromX;
\r
9892 explodeInfo.fromY = fromY;
\r
9893 explodeInfo.toX = toX;
\r
9894 explodeInfo.toY = toY;
\r
9895 for(i=1; i<4*kFactor; i++) {
\r
9896 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9897 DrawPosition(FALSE, board);
\r
9898 Sleep(appData.animSpeed);
\r
9900 explodeInfo.radius = 0;
\r
9901 DrawPosition(TRUE, board);
\r
9905 AnimateMove(board, fromX, fromY, toX, toY)
\r
9912 ChessSquare piece;
\r
9913 int x = toX, y = toY;
\r
9914 POINT start, finish, mid;
\r
9915 POINT frames[kFactor * 2 + 1];
\r
9918 if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();
\r
9920 if (!appData.animate) return;
\r
9921 if (doingSizing) return;
\r
9922 if (fromY < 0 || fromX < 0) return;
\r
9923 piece = board[fromY][fromX];
\r
9924 if (piece >= EmptySquare) return;
\r
9926 if(killX >= 0) toX = killX, toY = killY; // [HGM] lion: first to kill square
\r
9930 ScreenSquare(fromX, fromY, &start);
\r
9931 ScreenSquare(toX, toY, &finish);
\r
9933 /* All moves except knight jumps move in straight line */
\r
9934 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9935 mid.x = start.x + (finish.x - start.x) / 2;
\r
9936 mid.y = start.y + (finish.y - start.y) / 2;
\r
9938 /* Knight: make straight movement then diagonal */
\r
9939 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9940 mid.x = start.x + (finish.x - start.x) / 2;
\r
9944 mid.y = start.y + (finish.y - start.y) / 2;
\r
9948 /* Don't use as many frames for very short moves */
\r
9949 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9950 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9952 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9954 animInfo.from.x = fromX;
\r
9955 animInfo.from.y = fromY;
\r
9956 animInfo.to.x = toX;
\r
9957 animInfo.to.y = toY;
\r
9958 animInfo.lastpos = start;
\r
9959 animInfo.piece = piece;
\r
9960 for (n = 0; n < nFrames; n++) {
\r
9961 animInfo.pos = frames[n];
\r
9962 DrawPosition(FALSE, NULL);
\r
9963 animInfo.lastpos = animInfo.pos;
\r
9964 Sleep(appData.animSpeed);
\r
9966 animInfo.pos = finish;
\r
9967 DrawPosition(FALSE, NULL);
\r
9969 if(toX != x || toY != y) { fromX = toX; fromY = toY; toX = x; toY = y; goto again; } // second leg
\r
9971 animInfo.piece = EmptySquare;
\r
9972 Explode(board, fromX, fromY, toX, toY);
\r
9975 /* Convert board position to corner of screen rect and color */
\r
9978 ScreenSquare(column, row, pt)
\r
9979 int column; int row; POINT * pt;
\r
9982 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
9983 pt->y = lineGap + row * (squareSize + lineGap) + border;
\r
9985 pt->x = lineGap + column * (squareSize + lineGap) + border;
\r
9986 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
9990 /* Generate a series of frame coords from start->mid->finish.
\r
9991 The movement rate doubles until the half way point is
\r
9992 reached, then halves back down to the final destination,
\r
9993 which gives a nice slow in/out effect. The algorithmn
\r
9994 may seem to generate too many intermediates for short
\r
9995 moves, but remember that the purpose is to attract the
\r
9996 viewers attention to the piece about to be moved and
\r
9997 then to where it ends up. Too few frames would be less
\r
10001 Tween(start, mid, finish, factor, frames, nFrames)
\r
10002 POINT * start; POINT * mid;
\r
10003 POINT * finish; int factor;
\r
10004 POINT frames[]; int * nFrames;
\r
10006 int n, fraction = 1, count = 0;
\r
10008 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
10009 for (n = 0; n < factor; n++)
\r
10011 for (n = 0; n < factor; n++) {
\r
10012 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
10013 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
10015 fraction = fraction / 2;
\r
10019 frames[count] = *mid;
\r
10022 /* Slow out, stepping 1/2, then 1/4, ... */
\r
10024 for (n = 0; n < factor; n++) {
\r
10025 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
10026 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
10028 fraction = fraction * 2;
\r
10030 *nFrames = count;
\r
10034 SettingsPopUp(ChessProgramState *cps)
\r
10035 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
10036 EngineOptionsPopup(savedHwnd, cps);
\r
10039 int flock(int fid, int code)
\r
10041 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
10043 ov.hEvent = NULL;
\r
10045 ov.OffsetHigh = 0;
\r
10047 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
10048 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
10049 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
10050 default: return -1;
\r
10059 static char col[8][20];
\r
10060 COLORREF color = *(COLORREF *) colorVariable[n];
\r
10062 snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
10067 ActivateTheme (int new)
\r
10068 { // Redo initialization of features depending on options that can occur in themes
\r
10070 if(new) InitDrawingColors();
\r
10071 fontBitmapSquareSize = 0; // request creation of new font pieces
\r
10072 InitDrawingSizes(boardSize, 0);
\r
10073 InvalidateRect(hwndMain, NULL, TRUE);
\r