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
97 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
\r
100 void mysrandom(unsigned int seed);
\r
102 extern int whiteFlag, blackFlag;
\r
103 Boolean flipClock = FALSE;
\r
104 extern HANDLE chatHandle[];
\r
105 extern enum ICS_TYPE ics_type;
\r
107 int MySearchPath P((char *installDir, char *name, char *fullname));
\r
108 int MyGetFullPathName P((char *name, char *fullname));
\r
109 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
110 VOID NewVariantPopup(HWND hwnd);
\r
111 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
112 /*char*/int promoChar));
\r
113 void DisplayMove P((int moveNumber));
\r
114 void ChatPopUp P((char *s));
\r
116 ChessSquare piece;
\r
117 POINT pos; /* window coordinates of current pos */
\r
118 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
119 POINT from; /* board coordinates of the piece's orig pos */
\r
120 POINT to; /* board coordinates of the piece's new pos */
\r
123 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
126 POINT start; /* window coordinates of start pos */
\r
127 POINT pos; /* window coordinates of current pos */
\r
128 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
129 POINT from; /* board coordinates of the piece's orig pos */
\r
133 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };
\r
136 POINT sq[2]; /* board coordinates of from, to squares */
\r
139 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
140 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
141 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
142 static HighlightInfo oldPartnerHighlight = { {{-1, -1}, {-1, -1}} };
\r
144 typedef struct { // [HGM] atomic
\r
145 int fromX, fromY, toX, toY, radius;
\r
148 static ExplodeInfo explodeInfo;
\r
150 /* Window class names */
\r
151 char szAppName[] = "WinBoard";
\r
152 char szConsoleName[] = "WBConsole";
\r
154 /* Title bar text */
\r
155 char szTitle[] = "WinBoard";
\r
156 char szConsoleTitle[] = "I C S Interaction";
\r
159 char *settingsFileName;
\r
160 Boolean saveSettingsOnExit;
\r
161 char installDir[MSG_SIZ];
\r
162 int errorExitStatus;
\r
164 BoardSize boardSize;
\r
165 Boolean chessProgram;
\r
166 //static int boardX, boardY;
\r
167 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
168 int squareSize, lineGap, minorSize, border;
\r
169 static int winW, winH;
\r
170 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
171 static int logoHeight = 0;
\r
172 static char messageText[MESSAGE_TEXT_MAX];
\r
173 static int clockTimerEvent = 0;
\r
174 static int loadGameTimerEvent = 0;
\r
175 static int analysisTimerEvent = 0;
\r
176 static DelayedEventCallback delayedTimerCallback;
\r
177 static int delayedTimerEvent = 0;
\r
178 static int buttonCount = 2;
\r
179 char *icsTextMenuString;
\r
181 char *firstChessProgramNames;
\r
182 char *secondChessProgramNames;
\r
184 #define PALETTESIZE 256
\r
186 HINSTANCE hInst; /* current instance */
\r
187 Boolean alwaysOnTop = FALSE;
\r
189 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
190 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
191 COLORREF markerColor[8] = { 0x00FFFF, 0x0000FF, 0x00FF00, 0xFF0000, 0xFFFF00, 0xFF00FF, 0xFFFFFF, 0x000000 };
\r
193 ColorClass currentColorClass;
\r
195 static HWND savedHwnd;
\r
196 HWND hCommPort = NULL; /* currently open comm port */
\r
197 static HWND hwndPause; /* pause button */
\r
198 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
199 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
200 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
201 explodeBrush, /* [HGM] atomic */
\r
202 markerBrush[8], /* [HGM] markers */
\r
203 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
204 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
205 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
206 static HPEN gridPen = NULL;
\r
207 static HPEN highlightPen = NULL;
\r
208 static HPEN premovePen = NULL;
\r
209 static NPLOGPALETTE pLogPal;
\r
210 static BOOL paletteChanged = FALSE;
\r
211 static HICON iconWhite, iconBlack, iconCurrent;
\r
212 static int doingSizing = FALSE;
\r
213 static int lastSizing = 0;
\r
214 static int prevStderrPort;
\r
215 static HBITMAP userLogo;
\r
217 static HBITMAP liteBackTexture = NULL;
\r
218 static HBITMAP darkBackTexture = NULL;
\r
219 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
220 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
221 static int backTextureSquareSize = 0;
\r
222 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
224 #if __GNUC__ && !defined(_winmajor)
\r
225 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
228 #if defined(_winmajor)
\r
229 #define oldDialog (_winmajor < 4)
\r
231 #define oldDialog 0
\r
235 #define INTERNATIONAL
\r
237 #ifdef INTERNATIONAL
\r
238 # define _(s) T_(s)
\r
244 # define Translate(x, y)
\r
245 # define LoadLanguageFile(s)
\r
248 #ifdef INTERNATIONAL
\r
250 Boolean barbaric; // flag indicating if translation is needed
\r
252 // list of item numbers used in each dialog (used to alter language at run time)
\r
254 #define ABOUTBOX -1 /* not sure why these are needed */
\r
255 #define ABOUTBOX2 -1
\r
257 int dialogItems[][42] = {
\r
258 { ABOUTBOX, IDOK, OPT_MESS, 400 },
\r
259 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed,
\r
260 OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors, IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL },
\r
261 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,
\r
262 OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, IDOK, IDCANCEL },
\r
263 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
264 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
265 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
266 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
267 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
268 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
269 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
270 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
271 { ABOUTBOX2, IDC_ChessBoard },
\r
272 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
273 OPT_GameListClose, IDC_GameListDoFilter },
\r
274 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
275 { DLG_Error, IDOK },
\r
276 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
277 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
278 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
279 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
280 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
281 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
282 { DLG_IndexNumber, IDC_Index },
\r
283 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
284 { DLG_TypeInName, IDOK, IDCANCEL },
\r
285 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
286 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
287 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
288 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
289 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
290 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
291 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
292 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
293 OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove },
\r
294 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
295 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
296 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
297 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
298 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
299 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
300 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
301 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
302 GPB_General, GPB_Alarm, OPT_AutoCreate },
\r
303 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
304 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
305 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
306 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
307 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
308 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
309 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
310 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid },
\r
311 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
312 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
313 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
314 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
315 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
316 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
317 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,
\r
318 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
319 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
320 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
321 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
322 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,
\r
323 OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7,
\r
324 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
325 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
326 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
327 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
328 { DLG_MoveHistory },
\r
329 { DLG_EvalGraph },
\r
330 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
331 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
332 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
333 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
334 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
335 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
336 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
337 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
338 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
342 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];
\r
343 static int lastChecked;
\r
344 static char oldLanguage[MSG_SIZ], *menuText[10][30];
\r
345 extern int tinyLayout;
\r
346 extern char * menuBarText[][10];
\r
349 LoadLanguageFile(char *name)
\r
350 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
352 int i=0, j=0, n=0, k;
\r
355 if(!name || name[0] == NULLCHAR) return;
\r
356 snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension
\r
357 appData.language = oldLanguage;
\r
358 if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on
\r
359 if((f = fopen(buf, "r")) == NULL) return;
\r
360 while((k = fgetc(f)) != EOF) {
\r
361 if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }
\r
362 languageBuf[i] = k;
\r
364 if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {
\r
366 if(p = strstr(languageBuf + n + 1, "\" === \"")) {
\r
367 if(p > languageBuf+n+2 && p+8 < languageBuf+i) {
\r
368 if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }
\r
369 english[j] = languageBuf + n + 1; *p = 0;
\r
370 foreign[j++] = p + 7; languageBuf[i-1] = 0;
\r
371 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);
\r
376 } else if(i > 0 && languageBuf[i-1] == '\\') {
\r
378 case 'n': k = '\n'; break;
\r
379 case 'r': k = '\r'; break;
\r
380 case 't': k = '\t'; break;
\r
382 languageBuf[--i] = k;
\r
387 barbaric = (j != 0);
\r
388 safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );
\r
393 { // return the translation of the given string
\r
394 // efficiency can be improved a lot...
\r
396 static char buf[MSG_SIZ];
\r
397 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
398 if(!barbaric) return s;
\r
399 if(!s) return ""; // sanity
\r
400 while(english[i]) {
\r
401 if(!strcmp(s, english[i])) return foreign[i];
\r
402 if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending
\r
403 snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion
\r
412 Translate(HWND hDlg, int dialogID)
\r
413 { // translate all text items in the given dialog
\r
415 char buf[MSG_SIZ], *s;
\r
416 if(!barbaric) return;
\r
417 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
418 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
419 GetWindowText( hDlg, buf, MSG_SIZ );
\r
421 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
422 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
423 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
424 if(strlen(buf) == 0) continue;
\r
426 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
431 TranslateOneMenu(int i, HMENU subMenu)
\r
434 static MENUITEMINFO info;
\r
436 info.cbSize = sizeof(MENUITEMINFO);
\r
437 info.fMask = MIIM_STATE | MIIM_TYPE;
\r
438 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
440 info.dwTypeData = buf;
\r
441 info.cch = sizeof(buf);
\r
442 GetMenuItemInfo(subMenu, j, TRUE, &info);
\r
444 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );
\r
445 else menuText[i][j] = strdup(buf); // remember original on first change
\r
447 if(buf[0] == NULLCHAR) continue;
\r
448 info.dwTypeData = T_(buf);
\r
449 info.cch = strlen(buf)+1;
\r
450 SetMenuItemInfo(subMenu, j, TRUE, &info);
\r
456 TranslateMenus(int addLanguage)
\r
459 WIN32_FIND_DATA fileData;
\r
461 #define IDM_English 1970
\r
463 HMENU mainMenu = GetMenu(hwndMain);
\r
464 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
465 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
466 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
467 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
468 TranslateOneMenu(i, subMenu);
\r
470 DrawMenuBar(hwndMain);
\r
473 if(!addLanguage) return;
\r
474 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
475 HMENU mainMenu = GetMenu(hwndMain);
\r
476 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
477 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
478 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
479 i = 0; lastChecked = IDM_English;
\r
481 char *p, *q = fileData.cFileName;
\r
482 int checkFlag = MF_UNCHECKED;
\r
483 languageFile[i] = strdup(q);
\r
484 if(barbaric && !strcmp(oldLanguage, q)) {
\r
485 checkFlag = MF_CHECKED;
\r
486 lastChecked = IDM_English + i + 1;
\r
487 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
489 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
490 p = strstr(fileData.cFileName, ".lng");
\r
492 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
493 } while(FindNextFile(hFind, &fileData));
\r
500 #define IDM_RecentEngines 3000
\r
503 RecentEngineMenu (char *s)
\r
505 if(appData.icsActive) return;
\r
506 if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty
\r
507 HMENU mainMenu = GetMenu(hwndMain);
\r
508 HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu
\r
509 int i=IDM_RecentEngines;
\r
510 recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu
\r
511 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
513 char *p = strchr(s, '\n');
\r
514 if(p == NULL) return; // malformed!
\r
516 AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);
\r
530 int cliWidth, cliHeight;
\r
533 SizeInfo sizeInfo[] =
\r
535 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
536 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
537 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
538 { "petite", 33, 1, 1, 1, 0, 0 },
\r
539 { "slim", 37, 2, 1, 0, 0, 0 },
\r
540 { "small", 40, 2, 1, 0, 0, 0 },
\r
541 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
542 { "middling", 49, 2, 0, 0, 0, 0 },
\r
543 { "average", 54, 2, 0, 0, 0, 0 },
\r
544 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
545 { "medium", 64, 3, 0, 0, 0, 0 },
\r
546 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
547 { "large", 80, 3, 0, 0, 0, 0 },
\r
548 { "big", 87, 3, 0, 0, 0, 0 },
\r
549 { "huge", 95, 3, 0, 0, 0, 0 },
\r
550 { "giant", 108, 3, 0, 0, 0, 0 },
\r
551 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
552 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
553 { NULL, 0, 0, 0, 0, 0, 0 }
\r
556 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
557 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
559 { 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
560 { 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
561 { 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
562 { 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
563 { 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
564 { 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
565 { 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
566 { 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
567 { 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
568 { 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
569 { 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
570 { 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
571 { 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
572 { 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
573 { 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
574 { 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
575 { 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
576 { 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
579 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
588 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
589 #define N_BUTTONS 5
\r
591 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
593 {"<<", IDM_ToStart, NULL, NULL},
\r
594 {"<", IDM_Backward, NULL, NULL},
\r
595 {"P", IDM_Pause, NULL, NULL},
\r
596 {">", IDM_Forward, NULL, NULL},
\r
597 {">>", IDM_ToEnd, NULL, NULL},
\r
600 int tinyLayout = 0, smallLayout = 0;
\r
601 #define MENU_BAR_ITEMS 9
\r
602 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
603 { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },
\r
604 { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },
\r
608 MySound sounds[(int)NSoundClasses];
\r
609 MyTextAttribs textAttribs[(int)NColorClasses];
\r
611 MyColorizeAttribs colorizeAttribs[] = {
\r
612 { (COLORREF)0, 0, N_("Shout Text") },
\r
613 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
614 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
615 { (COLORREF)0, 0, N_("Channel Text") },
\r
616 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
617 { (COLORREF)0, 0, N_("Tell Text") },
\r
618 { (COLORREF)0, 0, N_("Challenge Text") },
\r
619 { (COLORREF)0, 0, N_("Request Text") },
\r
620 { (COLORREF)0, 0, N_("Seek Text") },
\r
621 { (COLORREF)0, 0, N_("Normal Text") },
\r
622 { (COLORREF)0, 0, N_("None") }
\r
627 static char *commentTitle;
\r
628 static char *commentText;
\r
629 static int commentIndex;
\r
630 static Boolean editComment = FALSE;
\r
633 char errorTitle[MSG_SIZ];
\r
634 char errorMessage[2*MSG_SIZ];
\r
635 HWND errorDialog = NULL;
\r
636 BOOLEAN moveErrorMessageUp = FALSE;
\r
637 BOOLEAN consoleEcho = TRUE;
\r
638 CHARFORMAT consoleCF;
\r
639 COLORREF consoleBackgroundColor;
\r
641 char *programVersion;
\r
647 typedef int CPKind;
\r
656 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
659 #define INPUT_SOURCE_BUF_SIZE 4096
\r
661 typedef struct _InputSource {
\r
668 char buf[INPUT_SOURCE_BUF_SIZE];
\r
672 InputCallback func;
\r
673 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
677 InputSource *consoleInputSource;
\r
682 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
683 VOID ConsoleCreate();
\r
685 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
686 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
687 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
688 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
690 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
691 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
692 void ParseIcsTextMenu(char *icsTextMenuString);
\r
693 VOID PopUpNameDialog(char firstchar);
\r
694 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
698 int GameListOptions();
\r
700 int dummy; // [HGM] for obsolete args
\r
702 HWND hwndMain = NULL; /* root window*/
\r
703 HWND hwndConsole = NULL;
\r
704 HWND commentDialog = NULL;
\r
705 HWND moveHistoryDialog = NULL;
\r
706 HWND evalGraphDialog = NULL;
\r
707 HWND engineOutputDialog = NULL;
\r
708 HWND gameListDialog = NULL;
\r
709 HWND editTagsDialog = NULL;
\r
711 int commentUp = FALSE;
\r
713 WindowPlacement wpMain;
\r
714 WindowPlacement wpConsole;
\r
715 WindowPlacement wpComment;
\r
716 WindowPlacement wpMoveHistory;
\r
717 WindowPlacement wpEvalGraph;
\r
718 WindowPlacement wpEngineOutput;
\r
719 WindowPlacement wpGameList;
\r
720 WindowPlacement wpTags;
\r
722 VOID EngineOptionsPopup(); // [HGM] settings
\r
724 VOID GothicPopUp(char *title, VariantClass variant);
\r
726 * Setting "frozen" should disable all user input other than deleting
\r
727 * the window. We do this while engines are initializing themselves.
\r
729 static int frozen = 0;
\r
730 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
736 if (frozen) return;
\r
738 hmenu = GetMenu(hwndMain);
\r
739 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
740 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
742 DrawMenuBar(hwndMain);
\r
745 /* Undo a FreezeUI */
\r
751 if (!frozen) return;
\r
753 hmenu = GetMenu(hwndMain);
\r
754 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
755 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
757 DrawMenuBar(hwndMain);
\r
760 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
762 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
768 #define JAWS_ALT_INTERCEPT
\r
769 #define JAWS_KBUP_NAVIGATION
\r
770 #define JAWS_KBDOWN_NAVIGATION
\r
771 #define JAWS_MENU_ITEMS
\r
772 #define JAWS_SILENCE
\r
773 #define JAWS_REPLAY
\r
775 #define JAWS_COPYRIGHT
\r
776 #define JAWS_DELETE(X) X
\r
777 #define SAYMACHINEMOVE()
\r
781 /*---------------------------------------------------------------------------*\
\r
785 \*---------------------------------------------------------------------------*/
\r
788 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
789 LPSTR lpCmdLine, int nCmdShow)
\r
792 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
793 // INITCOMMONCONTROLSEX ex;
\r
797 LoadLibrary("RICHED32.DLL");
\r
798 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
800 if (!InitApplication(hInstance)) {
\r
803 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
810 // InitCommonControlsEx(&ex);
\r
811 InitCommonControls();
\r
813 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
814 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
815 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
817 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
819 while (GetMessage(&msg, /* message structure */
\r
820 NULL, /* handle of window receiving the message */
\r
821 0, /* lowest message to examine */
\r
822 0)) /* highest message to examine */
\r
825 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
826 // [HGM] navigate: switch between all windows with tab
\r
827 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
828 int i, currentElement = 0;
\r
830 // first determine what element of the chain we come from (if any)
\r
831 if(appData.icsActive) {
\r
832 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
833 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
835 if(engineOutputDialog && EngineOutputIsUp()) {
\r
836 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
837 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
839 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
840 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
842 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
843 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
844 if(msg.hwnd == e1) currentElement = 2; else
\r
845 if(msg.hwnd == e2) currentElement = 3; else
\r
846 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
847 if(msg.hwnd == mh) currentElement = 4; else
\r
848 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
849 if(msg.hwnd == hText) currentElement = 5; else
\r
850 if(msg.hwnd == hInput) currentElement = 6; else
\r
851 for (i = 0; i < N_BUTTONS; i++) {
\r
852 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
855 // determine where to go to
\r
856 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
858 currentElement = (currentElement + direction) % 7;
\r
859 switch(currentElement) {
\r
861 h = hwndMain; break; // passing this case always makes the loop exit
\r
863 h = buttonDesc[0].hwnd; break; // could be NULL
\r
865 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
868 if(!EngineOutputIsUp()) continue;
\r
871 if(!MoveHistoryIsUp()) continue;
\r
873 // case 6: // input to eval graph does not seem to get here!
\r
874 // if(!EvalGraphIsUp()) continue;
\r
875 // h = evalGraphDialog; break;
\r
877 if(!appData.icsActive) continue;
\r
881 if(!appData.icsActive) continue;
\r
887 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
888 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
891 continue; // this message now has been processed
\r
895 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
896 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
897 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
898 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
899 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
900 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
901 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
902 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
903 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
904 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
905 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
906 for(i=0; i<MAX_CHAT; i++)
\r
907 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
910 if(done) continue; // [HGM] chat: end patch
\r
911 TranslateMessage(&msg); /* Translates virtual key codes */
\r
912 DispatchMessage(&msg); /* Dispatches message to window */
\r
917 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
920 /*---------------------------------------------------------------------------*\
\r
922 * Initialization functions
\r
924 \*---------------------------------------------------------------------------*/
\r
928 { // update user logo if necessary
\r
929 static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;
\r
931 if(appData.autoLogo) {
\r
932 curName = UserName();
\r
933 if(strcmp(curName, oldUserName)) {
\r
934 GetCurrentDirectory(MSG_SIZ, dir);
\r
935 SetCurrentDirectory(installDir);
\r
936 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
937 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
938 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
939 if(userLogo == NULL)
\r
940 userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
941 SetCurrentDirectory(dir); /* return to prev directory */
\r
947 InitApplication(HINSTANCE hInstance)
\r
951 /* Fill in window class structure with parameters that describe the */
\r
954 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
955 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
956 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
957 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
958 wc.hInstance = hInstance; /* Owner of this class */
\r
959 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
960 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
961 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
962 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
963 wc.lpszClassName = szAppName; /* Name to register as */
\r
965 /* Register the window class and return success/failure code. */
\r
966 if (!RegisterClass(&wc)) return FALSE;
\r
968 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
969 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
971 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
972 wc.hInstance = hInstance;
\r
973 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
974 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
975 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
976 wc.lpszMenuName = NULL;
\r
977 wc.lpszClassName = szConsoleName;
\r
979 if (!RegisterClass(&wc)) return FALSE;
\r
984 /* Set by InitInstance, used by EnsureOnScreen */
\r
985 int screenHeight, screenWidth;
\r
988 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
990 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
991 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
992 if (*x > screenWidth - 32) *x = 0;
\r
993 if (*y > screenHeight - 32) *y = 0;
\r
994 if (*x < minX) *x = minX;
\r
995 if (*y < minY) *y = minY;
\r
999 LoadLogo(ChessProgramState *cps, int n, Boolean ics)
\r
1001 char buf[MSG_SIZ], dir[MSG_SIZ];
\r
1002 GetCurrentDirectory(MSG_SIZ, dir);
\r
1003 SetCurrentDirectory(installDir);
\r
1004 if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {
\r
1005 cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1007 if (cps->programLogo == NULL && appData.debugMode) {
\r
1008 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );
\r
1010 } else if(appData.autoLogo) {
\r
1011 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
\r
1012 char *opponent = "";
\r
1013 if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;
\r
1014 if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;
\r
1015 sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);
\r
1016 if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {
\r
1017 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
1018 cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1021 if(appData.directory[n] && appData.directory[n][0]) {
\r
1022 SetCurrentDirectory(appData.directory[n]);
\r
1023 cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1026 SetCurrentDirectory(dir); /* return to prev directory */
\r
1032 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
1033 backTextureSquareSize = 0; // kludge to force recalculation of texturemode
\r
1035 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1036 if(liteBackTexture) DeleteObject(liteBackTexture);
\r
1037 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1038 liteBackTextureMode = appData.liteBackTextureMode;
\r
1040 if (liteBackTexture == NULL && appData.debugMode) {
\r
1041 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1045 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1046 if(darkBackTexture) DeleteObject(darkBackTexture);
\r
1047 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1048 darkBackTextureMode = appData.darkBackTextureMode;
\r
1050 if (darkBackTexture == NULL && appData.debugMode) {
\r
1051 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1057 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
1059 HWND hwnd; /* Main window handle. */
\r
1061 WINDOWPLACEMENT wp;
\r
1064 hInst = hInstance; /* Store instance handle in our global variable */
\r
1065 programName = szAppName;
\r
1067 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
1068 *filepart = NULLCHAR;
\r
1069 SetCurrentDirectory(installDir);
\r
1071 GetCurrentDirectory(MSG_SIZ, installDir);
\r
1073 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
1074 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
1075 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
1076 /* xboard, and older WinBoards, controlled the move sound with the
\r
1077 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
1078 always turn the option on (so that the backend will call us),
\r
1079 then let the user turn the sound off by setting it to silence if
\r
1080 desired. To accommodate old winboard.ini files saved by old
\r
1081 versions of WinBoard, we also turn off the sound if the option
\r
1082 was initially set to false. [HGM] taken out of InitAppData */
\r
1083 if (!appData.ringBellAfterMoves) {
\r
1084 sounds[(int)SoundMove].name = strdup("");
\r
1085 appData.ringBellAfterMoves = TRUE;
\r
1087 if (appData.debugMode) {
\r
1088 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
1089 setbuf(debugFP, NULL);
\r
1092 LoadLanguageFile(appData.language);
\r
1096 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
1097 // InitEngineUCI( installDir, &second );
\r
1099 /* Create a main window for this application instance. */
\r
1100 hwnd = CreateWindow(szAppName, szTitle,
\r
1101 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
1102 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
1103 NULL, NULL, hInstance, NULL);
\r
1106 /* If window could not be created, return "failure" */
\r
1111 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1112 LoadLogo(&first, 0, FALSE);
\r
1113 LoadLogo(&second, 1, appData.icsActive);
\r
1117 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1118 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1119 iconCurrent = iconWhite;
\r
1120 InitDrawingColors();
\r
1121 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1122 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1123 InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args
\r
1124 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1125 /* Compute window size for each board size, and use the largest
\r
1126 size that fits on this screen as the default. */
\r
1127 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1128 if (boardSize == (BoardSize)-1 &&
\r
1129 winH <= screenHeight
\r
1130 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1131 && winW <= screenWidth) {
\r
1132 boardSize = (BoardSize)ibs;
\r
1136 InitDrawingSizes(boardSize, 0);
\r
1137 RecentEngineMenu(appData.recentEngineList);
\r
1139 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1141 /* [AS] Load textures if specified */
\r
1144 mysrandom( (unsigned) time(NULL) );
\r
1146 /* [AS] Restore layout */
\r
1147 if( wpMoveHistory.visible ) {
\r
1148 MoveHistoryPopUp();
\r
1151 if( wpEvalGraph.visible ) {
\r
1155 if( wpEngineOutput.visible ) {
\r
1156 EngineOutputPopUp();
\r
1159 /* Make the window visible; update its client area; and return "success" */
\r
1160 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1161 wp.length = sizeof(WINDOWPLACEMENT);
\r
1163 wp.showCmd = nCmdShow;
\r
1164 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1165 wp.rcNormalPosition.left = wpMain.x;
\r
1166 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1167 wp.rcNormalPosition.top = wpMain.y;
\r
1168 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1169 SetWindowPlacement(hwndMain, &wp);
\r
1171 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1173 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1174 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1176 if (hwndConsole) {
\r
1178 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1179 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1181 ShowWindow(hwndConsole, nCmdShow);
\r
1182 SetActiveWindow(hwndConsole);
\r
1184 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1185 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1194 HMENU hmenu = GetMenu(hwndMain);
\r
1196 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1197 MF_BYCOMMAND|((appData.icsActive &&
\r
1198 *appData.icsCommPort != NULLCHAR) ?
\r
1199 MF_ENABLED : MF_GRAYED));
\r
1200 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1201 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1202 MF_CHECKED : MF_UNCHECKED));
\r
1205 //---------------------------------------------------------------------------------------------------------
\r
1207 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1208 #define XBOARD FALSE
\r
1210 #define OPTCHAR "/"
\r
1211 #define SEPCHAR "="
\r
1212 #define TOPLEVEL 0
\r
1216 // front-end part of option handling
\r
1219 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1221 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1222 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1225 lf->lfEscapement = 0;
\r
1226 lf->lfOrientation = 0;
\r
1227 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1228 lf->lfItalic = mfp->italic;
\r
1229 lf->lfUnderline = mfp->underline;
\r
1230 lf->lfStrikeOut = mfp->strikeout;
\r
1231 lf->lfCharSet = mfp->charset;
\r
1232 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1233 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1234 lf->lfQuality = DEFAULT_QUALITY;
\r
1235 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1236 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1240 CreateFontInMF(MyFont *mf)
\r
1242 LFfromMFP(&mf->lf, &mf->mfp);
\r
1243 if (mf->hf) DeleteObject(mf->hf);
\r
1244 mf->hf = CreateFontIndirect(&mf->lf);
\r
1247 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1249 colorVariable[] = {
\r
1250 &whitePieceColor,
\r
1251 &blackPieceColor,
\r
1252 &lightSquareColor,
\r
1253 &darkSquareColor,
\r
1254 &highlightSquareColor,
\r
1255 &premoveHighlightColor,
\r
1257 &consoleBackgroundColor,
\r
1258 &appData.fontForeColorWhite,
\r
1259 &appData.fontBackColorWhite,
\r
1260 &appData.fontForeColorBlack,
\r
1261 &appData.fontBackColorBlack,
\r
1262 &appData.evalHistColorWhite,
\r
1263 &appData.evalHistColorBlack,
\r
1264 &appData.highlightArrowColor,
\r
1267 /* Command line font name parser. NULL name means do nothing.
\r
1268 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1269 For backward compatibility, syntax without the colon is also
\r
1270 accepted, but font names with digits in them won't work in that case.
\r
1273 ParseFontName(char *name, MyFontParams *mfp)
\r
1276 if (name == NULL) return;
\r
1278 q = strchr(p, ':');
\r
1280 if (q - p >= sizeof(mfp->faceName))
\r
1281 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1282 memcpy(mfp->faceName, p, q - p);
\r
1283 mfp->faceName[q - p] = NULLCHAR;
\r
1286 q = mfp->faceName;
\r
1288 while (*p && !isdigit(*p)) {
\r
1290 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1291 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1293 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1296 if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);
\r
1297 mfp->pointSize = (float) atof(p);
\r
1298 mfp->bold = (strchr(p, 'b') != NULL);
\r
1299 mfp->italic = (strchr(p, 'i') != NULL);
\r
1300 mfp->underline = (strchr(p, 'u') != NULL);
\r
1301 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1302 mfp->charset = DEFAULT_CHARSET;
\r
1303 q = strchr(p, 'c');
\r
1305 mfp->charset = (BYTE) atoi(q+1);
\r
1309 ParseFont(char *name, int number)
\r
1310 { // wrapper to shield back-end from 'font'
\r
1311 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1316 { // in WB we have a 2D array of fonts; this initializes their description
\r
1318 /* Point font array elements to structures and
\r
1319 parse default font names */
\r
1320 for (i=0; i<NUM_FONTS; i++) {
\r
1321 for (j=0; j<NUM_SIZES; j++) {
\r
1322 font[j][i] = &fontRec[j][i];
\r
1323 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1330 { // here we create the actual fonts from the selected descriptions
\r
1332 for (i=0; i<NUM_FONTS; i++) {
\r
1333 for (j=0; j<NUM_SIZES; j++) {
\r
1334 CreateFontInMF(font[j][i]);
\r
1338 /* Color name parser.
\r
1339 X version accepts X color names, but this one
\r
1340 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1342 ParseColorName(char *name)
\r
1344 int red, green, blue, count;
\r
1345 char buf[MSG_SIZ];
\r
1347 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1349 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1350 &red, &green, &blue);
\r
1353 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1354 DisplayError(buf, 0);
\r
1355 return RGB(0, 0, 0);
\r
1357 return PALETTERGB(red, green, blue);
\r
1361 ParseColor(int n, char *name)
\r
1362 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1363 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1367 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1369 char *e = argValue;
\r
1373 if (*e == 'b') eff |= CFE_BOLD;
\r
1374 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1375 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1376 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1377 else if (*e == '#' || isdigit(*e)) break;
\r
1381 *color = ParseColorName(e);
\r
1385 ParseTextAttribs(ColorClass cc, char *s)
\r
1386 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1387 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1388 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1392 ParseBoardSize(void *addr, char *name)
\r
1393 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1394 BoardSize bs = SizeTiny;
\r
1395 while (sizeInfo[bs].name != NULL) {
\r
1396 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1397 *(BoardSize *)addr = bs;
\r
1402 ExitArgError(_("Unrecognized board size value"), name, TRUE);
\r
1407 { // [HGM] import name from appData first
\r
1410 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1411 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1412 textAttribs[cc].sound.data = NULL;
\r
1413 MyLoadSound(&textAttribs[cc].sound);
\r
1415 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1416 textAttribs[cc].sound.name = strdup("");
\r
1417 textAttribs[cc].sound.data = NULL;
\r
1419 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1420 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1421 sounds[sc].data = NULL;
\r
1422 MyLoadSound(&sounds[sc]);
\r
1427 SetCommPortDefaults()
\r
1429 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1430 dcb.DCBlength = sizeof(DCB);
\r
1431 dcb.BaudRate = 9600;
\r
1432 dcb.fBinary = TRUE;
\r
1433 dcb.fParity = FALSE;
\r
1434 dcb.fOutxCtsFlow = FALSE;
\r
1435 dcb.fOutxDsrFlow = FALSE;
\r
1436 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1437 dcb.fDsrSensitivity = FALSE;
\r
1438 dcb.fTXContinueOnXoff = TRUE;
\r
1439 dcb.fOutX = FALSE;
\r
1441 dcb.fNull = FALSE;
\r
1442 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1443 dcb.fAbortOnError = FALSE;
\r
1445 dcb.Parity = SPACEPARITY;
\r
1446 dcb.StopBits = ONESTOPBIT;
\r
1449 // [HGM] args: these three cases taken out to stay in front-end
\r
1451 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1452 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1453 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1454 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1456 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1457 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1458 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1459 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1460 ad->argName, mfp->faceName, mfp->pointSize,
\r
1461 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1462 mfp->bold ? "b" : "",
\r
1463 mfp->italic ? "i" : "",
\r
1464 mfp->underline ? "u" : "",
\r
1465 mfp->strikeout ? "s" : "",
\r
1466 (int)mfp->charset);
\r
1472 { // [HGM] copy the names from the internal WB variables to appData
\r
1475 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1476 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1477 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1478 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1482 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1483 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1484 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1485 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1486 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1487 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1488 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1489 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1490 (ta->effects) ? " " : "",
\r
1491 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1495 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1496 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1497 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1498 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1499 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1503 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1504 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1505 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1509 ParseCommPortSettings(char *s)
\r
1510 { // wrapper to keep dcb from back-end
\r
1511 ParseCommSettings(s, &dcb);
\r
1516 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1517 GetActualPlacement(hwndMain, &wpMain);
\r
1518 GetActualPlacement(hwndConsole, &wpConsole);
\r
1519 GetActualPlacement(commentDialog, &wpComment);
\r
1520 GetActualPlacement(editTagsDialog, &wpTags);
\r
1521 GetActualPlacement(gameListDialog, &wpGameList);
\r
1522 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1523 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1524 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1528 PrintCommPortSettings(FILE *f, char *name)
\r
1529 { // wrapper to shield back-end from DCB
\r
1530 PrintCommSettings(f, name, &dcb);
\r
1534 MySearchPath(char *installDir, char *name, char *fullname)
\r
1536 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1537 if(name[0]== '%') {
\r
1538 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1539 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1540 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1541 *strchr(buf, '%') = 0;
\r
1542 strcat(fullname, getenv(buf));
\r
1543 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1545 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1546 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1547 return (int) strlen(fullname);
\r
1549 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1553 MyGetFullPathName(char *name, char *fullname)
\r
1556 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1561 { // [HGM] args: allows testing if main window is realized from back-end
\r
1562 return hwndMain != NULL;
\r
1566 PopUpStartupDialog()
\r
1570 LoadLanguageFile(appData.language);
\r
1571 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1572 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1573 FreeProcInstance(lpProc);
\r
1576 /*---------------------------------------------------------------------------*\
\r
1578 * GDI board drawing routines
\r
1580 \*---------------------------------------------------------------------------*/
\r
1582 /* [AS] Draw square using background texture */
\r
1583 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1588 return; /* Should never happen! */
\r
1591 SetGraphicsMode( dst, GM_ADVANCED );
\r
1598 /* X reflection */
\r
1603 x.eDx = (FLOAT) dw + dx - 1;
\r
1606 SetWorldTransform( dst, &x );
\r
1609 /* Y reflection */
\r
1615 x.eDy = (FLOAT) dh + dy - 1;
\r
1617 SetWorldTransform( dst, &x );
\r
1625 x.eDx = (FLOAT) dx;
\r
1626 x.eDy = (FLOAT) dy;
\r
1629 SetWorldTransform( dst, &x );
\r
1633 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1641 SetWorldTransform( dst, &x );
\r
1643 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1646 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1648 PM_WP = (int) WhitePawn,
\r
1649 PM_WN = (int) WhiteKnight,
\r
1650 PM_WB = (int) WhiteBishop,
\r
1651 PM_WR = (int) WhiteRook,
\r
1652 PM_WQ = (int) WhiteQueen,
\r
1653 PM_WF = (int) WhiteFerz,
\r
1654 PM_WW = (int) WhiteWazir,
\r
1655 PM_WE = (int) WhiteAlfil,
\r
1656 PM_WM = (int) WhiteMan,
\r
1657 PM_WO = (int) WhiteCannon,
\r
1658 PM_WU = (int) WhiteUnicorn,
\r
1659 PM_WH = (int) WhiteNightrider,
\r
1660 PM_WA = (int) WhiteAngel,
\r
1661 PM_WC = (int) WhiteMarshall,
\r
1662 PM_WAB = (int) WhiteCardinal,
\r
1663 PM_WD = (int) WhiteDragon,
\r
1664 PM_WL = (int) WhiteLance,
\r
1665 PM_WS = (int) WhiteCobra,
\r
1666 PM_WV = (int) WhiteFalcon,
\r
1667 PM_WSG = (int) WhiteSilver,
\r
1668 PM_WG = (int) WhiteGrasshopper,
\r
1669 PM_WK = (int) WhiteKing,
\r
1670 PM_BP = (int) BlackPawn,
\r
1671 PM_BN = (int) BlackKnight,
\r
1672 PM_BB = (int) BlackBishop,
\r
1673 PM_BR = (int) BlackRook,
\r
1674 PM_BQ = (int) BlackQueen,
\r
1675 PM_BF = (int) BlackFerz,
\r
1676 PM_BW = (int) BlackWazir,
\r
1677 PM_BE = (int) BlackAlfil,
\r
1678 PM_BM = (int) BlackMan,
\r
1679 PM_BO = (int) BlackCannon,
\r
1680 PM_BU = (int) BlackUnicorn,
\r
1681 PM_BH = (int) BlackNightrider,
\r
1682 PM_BA = (int) BlackAngel,
\r
1683 PM_BC = (int) BlackMarshall,
\r
1684 PM_BG = (int) BlackGrasshopper,
\r
1685 PM_BAB = (int) BlackCardinal,
\r
1686 PM_BD = (int) BlackDragon,
\r
1687 PM_BL = (int) BlackLance,
\r
1688 PM_BS = (int) BlackCobra,
\r
1689 PM_BV = (int) BlackFalcon,
\r
1690 PM_BSG = (int) BlackSilver,
\r
1691 PM_BK = (int) BlackKing
\r
1694 static HFONT hPieceFont = NULL;
\r
1695 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1696 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1697 static int fontBitmapSquareSize = 0;
\r
1698 static char pieceToFontChar[(int) EmptySquare] =
\r
1699 { 'p', 'n', 'b', 'r', 'q',
\r
1700 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1701 'k', 'o', 'm', 'v', 't', 'w',
\r
1702 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1705 extern BOOL SetCharTable( char *table, const char * map );
\r
1706 /* [HGM] moved to backend.c */
\r
1708 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1711 BYTE r1 = GetRValue( color );
\r
1712 BYTE g1 = GetGValue( color );
\r
1713 BYTE b1 = GetBValue( color );
\r
1719 /* Create a uniform background first */
\r
1720 hbrush = CreateSolidBrush( color );
\r
1721 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1722 FillRect( hdc, &rc, hbrush );
\r
1723 DeleteObject( hbrush );
\r
1726 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1727 int steps = squareSize / 2;
\r
1730 for( i=0; i<steps; i++ ) {
\r
1731 BYTE r = r1 - (r1-r2) * i / steps;
\r
1732 BYTE g = g1 - (g1-g2) * i / steps;
\r
1733 BYTE b = b1 - (b1-b2) * i / steps;
\r
1735 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1736 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1737 FillRect( hdc, &rc, hbrush );
\r
1738 DeleteObject(hbrush);
\r
1741 else if( mode == 2 ) {
\r
1742 /* Diagonal gradient, good more or less for every piece */
\r
1743 POINT triangle[3];
\r
1744 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1745 HBRUSH hbrush_old;
\r
1746 int steps = squareSize;
\r
1749 triangle[0].x = squareSize - steps;
\r
1750 triangle[0].y = squareSize;
\r
1751 triangle[1].x = squareSize;
\r
1752 triangle[1].y = squareSize;
\r
1753 triangle[2].x = squareSize;
\r
1754 triangle[2].y = squareSize - steps;
\r
1756 for( i=0; i<steps; i++ ) {
\r
1757 BYTE r = r1 - (r1-r2) * i / steps;
\r
1758 BYTE g = g1 - (g1-g2) * i / steps;
\r
1759 BYTE b = b1 - (b1-b2) * i / steps;
\r
1761 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1762 hbrush_old = SelectObject( hdc, hbrush );
\r
1763 Polygon( hdc, triangle, 3 );
\r
1764 SelectObject( hdc, hbrush_old );
\r
1765 DeleteObject(hbrush);
\r
1770 SelectObject( hdc, hpen );
\r
1775 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1776 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1777 piece: follow the steps as explained below.
\r
1779 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1783 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1787 int backColor = whitePieceColor;
\r
1788 int foreColor = blackPieceColor;
\r
1790 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1791 backColor = appData.fontBackColorWhite;
\r
1792 foreColor = appData.fontForeColorWhite;
\r
1794 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1795 backColor = appData.fontBackColorBlack;
\r
1796 foreColor = appData.fontForeColorBlack;
\r
1800 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1802 hbm_old = SelectObject( hdc, hbm );
\r
1806 rc.right = squareSize;
\r
1807 rc.bottom = squareSize;
\r
1809 /* Step 1: background is now black */
\r
1810 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1812 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1814 pt.x = (squareSize - sz.cx) / 2;
\r
1815 pt.y = (squareSize - sz.cy) / 2;
\r
1817 SetBkMode( hdc, TRANSPARENT );
\r
1818 SetTextColor( hdc, chroma );
\r
1819 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1820 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1822 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1823 /* Step 3: the area outside the piece is filled with white */
\r
1824 // FloodFill( hdc, 0, 0, chroma );
\r
1825 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1826 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1827 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1828 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1829 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1831 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1832 but if the start point is not inside the piece we're lost!
\r
1833 There should be a better way to do this... if we could create a region or path
\r
1834 from the fill operation we would be fine for example.
\r
1836 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1837 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1839 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1840 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1841 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1843 SelectObject( dc2, bm2 );
\r
1844 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1845 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1846 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1847 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1848 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1851 DeleteObject( bm2 );
\r
1854 SetTextColor( hdc, 0 );
\r
1856 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1857 draw the piece again in black for safety.
\r
1859 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1861 SelectObject( hdc, hbm_old );
\r
1863 if( hPieceMask[index] != NULL ) {
\r
1864 DeleteObject( hPieceMask[index] );
\r
1867 hPieceMask[index] = hbm;
\r
1870 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1872 SelectObject( hdc, hbm );
\r
1875 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1876 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1877 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1879 SelectObject( dc1, hPieceMask[index] );
\r
1880 SelectObject( dc2, bm2 );
\r
1881 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1882 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1885 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1886 the piece background and deletes (makes transparent) the rest.
\r
1887 Thanks to that mask, we are free to paint the background with the greates
\r
1888 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1889 We use this, to make gradients and give the pieces a "roundish" look.
\r
1891 SetPieceBackground( hdc, backColor, 2 );
\r
1892 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1896 DeleteObject( bm2 );
\r
1899 SetTextColor( hdc, foreColor );
\r
1900 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1902 SelectObject( hdc, hbm_old );
\r
1904 if( hPieceFace[index] != NULL ) {
\r
1905 DeleteObject( hPieceFace[index] );
\r
1908 hPieceFace[index] = hbm;
\r
1911 static int TranslatePieceToFontPiece( int piece )
\r
1941 case BlackMarshall:
\r
1945 case BlackNightrider:
\r
1951 case BlackUnicorn:
\r
1955 case BlackGrasshopper:
\r
1967 case BlackCardinal:
\r
1974 case WhiteMarshall:
\r
1978 case WhiteNightrider:
\r
1984 case WhiteUnicorn:
\r
1988 case WhiteGrasshopper:
\r
2000 case WhiteCardinal:
\r
2009 void CreatePiecesFromFont()
\r
2012 HDC hdc_window = NULL;
\r
2018 if( fontBitmapSquareSize < 0 ) {
\r
2019 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
2023 if( !appData.useFont || appData.renderPiecesWithFont == NULL ||
\r
2024 appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
2025 fontBitmapSquareSize = -1;
\r
2029 if( fontBitmapSquareSize != squareSize ) {
\r
2030 hdc_window = GetDC( hwndMain );
\r
2031 hdc = CreateCompatibleDC( hdc_window );
\r
2033 if( hPieceFont != NULL ) {
\r
2034 DeleteObject( hPieceFont );
\r
2037 for( i=0; i<=(int)BlackKing; i++ ) {
\r
2038 hPieceMask[i] = NULL;
\r
2039 hPieceFace[i] = NULL;
\r
2045 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
2046 fontHeight = appData.fontPieceSize;
\r
2049 fontHeight = (fontHeight * squareSize) / 100;
\r
2051 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2053 lf.lfEscapement = 0;
\r
2054 lf.lfOrientation = 0;
\r
2055 lf.lfWeight = FW_NORMAL;
\r
2057 lf.lfUnderline = 0;
\r
2058 lf.lfStrikeOut = 0;
\r
2059 lf.lfCharSet = DEFAULT_CHARSET;
\r
2060 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2061 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2062 lf.lfQuality = PROOF_QUALITY;
\r
2063 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2064 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2065 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2067 hPieceFont = CreateFontIndirect( &lf );
\r
2069 if( hPieceFont == NULL ) {
\r
2070 fontBitmapSquareSize = -2;
\r
2073 /* Setup font-to-piece character table */
\r
2074 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2075 /* No (or wrong) global settings, try to detect the font */
\r
2076 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2078 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2080 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2081 /* DiagramTT* family */
\r
2082 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2084 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2085 /* Fairy symbols */
\r
2086 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2088 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2089 /* Good Companion (Some characters get warped as literal :-( */
\r
2090 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2091 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2092 SetCharTable(pieceToFontChar, s);
\r
2095 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2096 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2100 /* Create bitmaps */
\r
2101 hfont_old = SelectObject( hdc, hPieceFont );
\r
2102 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2103 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2104 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2106 SelectObject( hdc, hfont_old );
\r
2108 fontBitmapSquareSize = squareSize;
\r
2112 if( hdc != NULL ) {
\r
2116 if( hdc_window != NULL ) {
\r
2117 ReleaseDC( hwndMain, hdc_window );
\r
2122 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2124 char name[128], buf[MSG_SIZ];
\r
2126 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2127 if(appData.pieceDirectory[0]) {
\r
2129 snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);
\r
2130 res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
2131 if(res) return res;
\r
2133 if (gameInfo.event &&
\r
2134 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2135 strcmp(name, "k80s") == 0) {
\r
2136 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2138 return LoadBitmap(hinst, name);
\r
2142 /* Insert a color into the program's logical palette
\r
2143 structure. This code assumes the given color is
\r
2144 the result of the RGB or PALETTERGB macro, and it
\r
2145 knows how those macros work (which is documented).
\r
2148 InsertInPalette(COLORREF color)
\r
2150 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2152 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2153 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2154 pLogPal->palNumEntries--;
\r
2158 pe->peFlags = (char) 0;
\r
2159 pe->peRed = (char) (0xFF & color);
\r
2160 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2161 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2167 InitDrawingColors()
\r
2170 if (pLogPal == NULL) {
\r
2171 /* Allocate enough memory for a logical palette with
\r
2172 * PALETTESIZE entries and set the size and version fields
\r
2173 * of the logical palette structure.
\r
2175 pLogPal = (NPLOGPALETTE)
\r
2176 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2177 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2178 pLogPal->palVersion = 0x300;
\r
2180 pLogPal->palNumEntries = 0;
\r
2182 InsertInPalette(lightSquareColor);
\r
2183 InsertInPalette(darkSquareColor);
\r
2184 InsertInPalette(whitePieceColor);
\r
2185 InsertInPalette(blackPieceColor);
\r
2186 InsertInPalette(highlightSquareColor);
\r
2187 InsertInPalette(premoveHighlightColor);
\r
2189 /* create a logical color palette according the information
\r
2190 * in the LOGPALETTE structure.
\r
2192 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2194 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2195 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2196 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2197 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2198 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2199 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2200 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2201 for(i=0; i<8;i++) markerBrush[i] = CreateSolidBrush(markerColor[i]); // [HGM] markers
\r
2203 /* [AS] Force rendering of the font-based pieces */
\r
2204 if( fontBitmapSquareSize > 0 ) {
\r
2205 fontBitmapSquareSize = 0;
\r
2211 BoardWidth(int boardSize, int n)
\r
2212 { /* [HGM] argument n added to allow different width and height */
\r
2213 int lineGap = sizeInfo[boardSize].lineGap;
\r
2215 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2216 lineGap = appData.overrideLineGap;
\r
2219 return (n + 1) * lineGap +
\r
2220 n * sizeInfo[boardSize].squareSize;
\r
2223 /* Respond to board resize by dragging edge */
\r
2225 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2227 BoardSize newSize = NUM_SIZES - 1;
\r
2228 static int recurse = 0;
\r
2229 if (IsIconic(hwndMain)) return;
\r
2230 if (recurse > 0) return;
\r
2232 while (newSize > 0) {
\r
2233 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2234 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2235 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2238 boardSize = newSize;
\r
2239 InitDrawingSizes(boardSize, flags);
\r
2244 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2247 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2249 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2250 ChessSquare piece;
\r
2251 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2253 SIZE clockSize, messageSize;
\r
2255 char buf[MSG_SIZ];
\r
2257 HMENU hmenu = GetMenu(hwndMain);
\r
2258 RECT crect, wrect, oldRect;
\r
2260 LOGBRUSH logbrush;
\r
2261 VariantClass v = gameInfo.variant;
\r
2263 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2264 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2266 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2267 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2268 if(boardSize == -1) return; // no size defined yet; abort (to allow early call of InitPosition)
\r
2269 oldBoardSize = boardSize;
\r
2271 if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)
\r
2272 { // correct board size to one where built-in pieces exist
\r
2273 if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)
\r
2274 && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range
\r
2275 || (v == VariantShogi && boardSize != SizeModerate) // Japanese-style Shogi
\r
2276 || v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan
\r
2277 || v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy || v == VariantLion ) {
\r
2278 if(boardSize < SizeMediocre) boardSize = SizePetite; else
\r
2279 if(boardSize > SizeModerate) boardSize = SizeBulky; else
\r
2280 boardSize = SizeMiddling;
\r
2283 if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite
\r
2285 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2286 oldRect.top = wpMain.y;
\r
2287 oldRect.right = wpMain.x + wpMain.width;
\r
2288 oldRect.bottom = wpMain.y + wpMain.height;
\r
2290 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2291 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2292 squareSize = sizeInfo[boardSize].squareSize;
\r
2293 lineGap = sizeInfo[boardSize].lineGap;
\r
2294 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2295 border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;
\r
2297 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2298 lineGap = appData.overrideLineGap;
\r
2301 if (tinyLayout != oldTinyLayout) {
\r
2302 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2304 style &= ~WS_SYSMENU;
\r
2305 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2306 "&Minimize\tCtrl+F4");
\r
2308 style |= WS_SYSMENU;
\r
2309 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2311 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2313 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2314 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2315 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2317 DrawMenuBar(hwndMain);
\r
2320 boardWidth = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;
\r
2321 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;
\r
2323 /* Get text area sizes */
\r
2324 hdc = GetDC(hwndMain);
\r
2325 if (appData.clockMode) {
\r
2326 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2328 snprintf(buf, MSG_SIZ, _("White"));
\r
2330 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2331 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2332 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2333 str = _("We only care about the height here");
\r
2334 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2335 SelectObject(hdc, oldFont);
\r
2336 ReleaseDC(hwndMain, hdc);
\r
2338 /* Compute where everything goes */
\r
2339 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2340 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2341 logoHeight = 2*clockSize.cy;
\r
2342 leftLogoRect.left = OUTER_MARGIN;
\r
2343 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2344 leftLogoRect.top = OUTER_MARGIN;
\r
2345 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2347 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2348 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2349 rightLogoRect.top = OUTER_MARGIN;
\r
2350 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2353 whiteRect.left = leftLogoRect.right;
\r
2354 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2355 whiteRect.top = OUTER_MARGIN;
\r
2356 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2358 blackRect.right = rightLogoRect.left;
\r
2359 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2360 blackRect.top = whiteRect.top;
\r
2361 blackRect.bottom = whiteRect.bottom;
\r
2363 whiteRect.left = OUTER_MARGIN;
\r
2364 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2365 whiteRect.top = OUTER_MARGIN;
\r
2366 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2368 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2369 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2370 blackRect.top = whiteRect.top;
\r
2371 blackRect.bottom = whiteRect.bottom;
\r
2373 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2376 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2377 if (appData.showButtonBar) {
\r
2378 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2379 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2381 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2383 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2384 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2386 boardRect.left = OUTER_MARGIN;
\r
2387 boardRect.right = boardRect.left + boardWidth;
\r
2388 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2389 boardRect.bottom = boardRect.top + boardHeight;
\r
2391 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2392 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2393 oldTinyLayout = tinyLayout;
\r
2394 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2395 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2396 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2397 winW *= 1 + twoBoards;
\r
2398 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2399 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2400 wpMain.height = winH; // without disturbing window attachments
\r
2401 GetWindowRect(hwndMain, &wrect);
\r
2402 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2403 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2405 // [HGM] placement: let attached windows follow size change.
\r
2406 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2407 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2408 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2409 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2410 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2412 /* compensate if menu bar wrapped */
\r
2413 GetClientRect(hwndMain, &crect);
\r
2414 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2415 wpMain.height += offby;
\r
2417 case WMSZ_TOPLEFT:
\r
2418 SetWindowPos(hwndMain, NULL,
\r
2419 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2420 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2423 case WMSZ_TOPRIGHT:
\r
2425 SetWindowPos(hwndMain, NULL,
\r
2426 wrect.left, wrect.bottom - wpMain.height,
\r
2427 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2430 case WMSZ_BOTTOMLEFT:
\r
2432 SetWindowPos(hwndMain, NULL,
\r
2433 wrect.right - wpMain.width, wrect.top,
\r
2434 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2437 case WMSZ_BOTTOMRIGHT:
\r
2441 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2442 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2447 for (i = 0; i < N_BUTTONS; i++) {
\r
2448 if (buttonDesc[i].hwnd != NULL) {
\r
2449 DestroyWindow(buttonDesc[i].hwnd);
\r
2450 buttonDesc[i].hwnd = NULL;
\r
2452 if (appData.showButtonBar) {
\r
2453 buttonDesc[i].hwnd =
\r
2454 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2455 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2456 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2457 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2458 (HMENU) buttonDesc[i].id,
\r
2459 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2461 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2462 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2463 MAKELPARAM(FALSE, 0));
\r
2465 if (buttonDesc[i].id == IDM_Pause)
\r
2466 hwndPause = buttonDesc[i].hwnd;
\r
2467 buttonDesc[i].wndproc = (WNDPROC)
\r
2468 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2471 if (gridPen != NULL) DeleteObject(gridPen);
\r
2472 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2473 if (premovePen != NULL) DeleteObject(premovePen);
\r
2474 if (lineGap != 0) {
\r
2475 logbrush.lbStyle = BS_SOLID;
\r
2476 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2478 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2479 lineGap, &logbrush, 0, NULL);
\r
2480 logbrush.lbColor = highlightSquareColor;
\r
2482 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2483 lineGap, &logbrush, 0, NULL);
\r
2485 logbrush.lbColor = premoveHighlightColor;
\r
2487 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2488 lineGap, &logbrush, 0, NULL);
\r
2490 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2491 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2492 gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;
\r
2493 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2494 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;
\r
2495 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2496 BOARD_WIDTH * (squareSize + lineGap) + border;
\r
2497 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2499 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2500 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;
\r
2501 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2502 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2503 lineGap / 2 + (i * (squareSize + lineGap)) + border;
\r
2504 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2505 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;
\r
2506 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2510 /* [HGM] Licensing requirement */
\r
2512 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2515 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2517 GothicPopUp( "", VariantNormal);
\r
2520 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2522 /* Load piece bitmaps for this board size */
\r
2523 for (i=0; i<=2; i++) {
\r
2524 for (piece = WhitePawn;
\r
2525 (int) piece < (int) BlackPawn;
\r
2526 piece = (ChessSquare) ((int) piece + 1)) {
\r
2527 if (pieceBitmap[i][piece] != NULL)
\r
2528 DeleteObject(pieceBitmap[i][piece]);
\r
2532 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2533 // Orthodox Chess pieces
\r
2534 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2535 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2536 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2537 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2538 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2539 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2540 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2541 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2542 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2543 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2544 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2545 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2546 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2547 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2548 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2549 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2550 // in Shogi, Hijack the unused Queen for Lance
\r
2551 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2552 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2553 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2555 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2556 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2557 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2560 if(squareSize <= 72 && squareSize >= 33) {
\r
2561 /* A & C are available in most sizes now */
\r
2562 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2563 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2564 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2565 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2566 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2567 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2568 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2569 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2570 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2571 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2572 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2573 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2574 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2575 } else { // Smirf-like
\r
2576 if(gameInfo.variant == VariantSChess) {
\r
2577 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2578 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2579 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2581 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2582 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2583 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2586 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2587 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2588 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2589 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2590 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2591 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2592 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2593 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2594 } else { // WinBoard standard
\r
2595 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2596 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2597 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2602 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2603 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2604 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2605 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2606 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2607 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2608 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2609 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2610 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2611 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2612 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2613 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2614 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2615 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2616 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2617 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2618 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2619 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2620 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2621 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2622 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2623 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2624 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2625 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2626 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2627 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2628 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2629 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2630 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2631 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2632 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2633 pieceBitmap[0][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "s");
\r
2634 pieceBitmap[1][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "o");
\r
2635 pieceBitmap[2][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "w");
\r
2637 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2638 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2639 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2640 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2641 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2642 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2643 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2644 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2645 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2646 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2647 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2648 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2649 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2651 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2652 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2653 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2654 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2655 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2656 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2657 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2658 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2659 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2660 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2661 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2662 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2665 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2666 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2667 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2668 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2669 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2670 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2671 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2672 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2673 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2674 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2675 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2676 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2677 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2678 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2679 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2683 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2684 /* special Shogi support in this size */
\r
2685 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2686 for (piece = WhitePawn;
\r
2687 (int) piece < (int) BlackPawn;
\r
2688 piece = (ChessSquare) ((int) piece + 1)) {
\r
2689 if (pieceBitmap[i][piece] != NULL)
\r
2690 DeleteObject(pieceBitmap[i][piece]);
\r
2693 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2694 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2695 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2696 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2697 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2698 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2699 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2700 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2701 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2702 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2703 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2704 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2705 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2706 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2707 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2708 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2709 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2710 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2711 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2712 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2713 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2714 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2715 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2716 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2717 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2718 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2719 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2720 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2721 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2722 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2723 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2724 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2725 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2726 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2727 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2728 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2729 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2730 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2731 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2732 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2733 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2734 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2740 PieceBitmap(ChessSquare p, int kind)
\r
2742 if ((int) p >= (int) BlackPawn)
\r
2743 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2745 return pieceBitmap[kind][(int) p];
\r
2748 /***************************************************************/
\r
2750 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2751 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2753 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2754 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2758 SquareToPos(int row, int column, int * x, int * y)
\r
2761 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
2762 *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;
\r
2764 *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;
\r
2765 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
2770 DrawCoordsOnDC(HDC hdc)
\r
2772 static char files[] = "0123456789012345678901221098765432109876543210";
\r
2773 static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";
\r
2774 char str[2] = { NULLCHAR, NULLCHAR };
\r
2775 int oldMode, oldAlign, x, y, start, i;
\r
2779 if (!appData.showCoords)
\r
2782 start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;
\r
2784 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2785 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2786 oldAlign = GetTextAlign(hdc);
\r
2787 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2789 y = boardRect.top + lineGap;
\r
2790 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2793 SetTextAlign(hdc, TA_RIGHT|TA_TOP);
\r
2794 x += border - lineGap - 4; y += squareSize - 6;
\r
2796 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2797 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2798 str[0] = files[start + i];
\r
2799 ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);
\r
2800 y += squareSize + lineGap;
\r
2803 start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;
\r
2806 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2807 x += -border + 4; y += border - squareSize + 6;
\r
2809 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2810 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2811 str[0] = ranks[start + i];
\r
2812 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2813 x += squareSize + lineGap;
\r
2816 SelectObject(hdc, oldBrush);
\r
2817 SetBkMode(hdc, oldMode);
\r
2818 SetTextAlign(hdc, oldAlign);
\r
2819 SelectObject(hdc, oldFont);
\r
2823 DrawGridOnDC(HDC hdc)
\r
2827 if (lineGap != 0) {
\r
2828 oldPen = SelectObject(hdc, gridPen);
\r
2829 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2830 SelectObject(hdc, oldPen);
\r
2834 #define HIGHLIGHT_PEN 0
\r
2835 #define PREMOVE_PEN 1
\r
2838 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2841 HPEN oldPen, hPen;
\r
2842 if (lineGap == 0) return;
\r
2844 x1 = boardRect.left +
\r
2845 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;
\r
2846 y1 = boardRect.top +
\r
2847 lineGap/2 + y * (squareSize + lineGap) + border;
\r
2849 x1 = boardRect.left +
\r
2850 lineGap/2 + x * (squareSize + lineGap) + border;
\r
2851 y1 = boardRect.top +
\r
2852 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;
\r
2854 hPen = pen ? premovePen : highlightPen;
\r
2855 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2856 MoveToEx(hdc, x1, y1, NULL);
\r
2857 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2858 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2859 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2860 LineTo(hdc, x1, y1);
\r
2861 SelectObject(hdc, oldPen);
\r
2865 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2868 for (i=0; i<2; i++) {
\r
2869 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2870 DrawHighlightOnDC(hdc, TRUE,
\r
2871 h->sq[i].x, h->sq[i].y,
\r
2876 /* Note: sqcolor is used only in monoMode */
\r
2877 /* Note that this code is largely duplicated in woptions.c,
\r
2878 function DrawSampleSquare, so that needs to be updated too */
\r
2880 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2882 HBITMAP oldBitmap;
\r
2886 if (appData.blindfold) return;
\r
2888 /* [AS] Use font-based pieces if needed */
\r
2889 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2890 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2891 CreatePiecesFromFont();
\r
2893 if( fontBitmapSquareSize == squareSize ) {
\r
2894 int index = TranslatePieceToFontPiece(piece);
\r
2896 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2898 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2899 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2903 squareSize, squareSize,
\r
2908 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2910 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2911 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2915 squareSize, squareSize,
\r
2924 if (appData.monoMode) {
\r
2925 SelectObject(tmphdc, PieceBitmap(piece,
\r
2926 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2927 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2928 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2930 HBRUSH xBrush = whitePieceBrush;
\r
2931 tmpSize = squareSize;
\r
2932 if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);
\r
2934 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2935 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2936 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2937 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2938 x += (squareSize - minorSize)>>1;
\r
2939 y += squareSize - minorSize - 2;
\r
2940 tmpSize = minorSize;
\r
2942 if (color || appData.allWhite ) {
\r
2943 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2945 oldBrush = SelectObject(hdc, xBrush);
\r
2946 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2947 if(appData.upsideDown && color==flipView)
\r
2948 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2950 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2951 /* Use black for outline of white pieces */
\r
2952 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2953 if(appData.upsideDown && color==flipView)
\r
2954 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2956 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2957 } else if(appData.pieceDirectory[0]) {
\r
2958 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2959 oldBrush = SelectObject(hdc, xBrush);
\r
2960 if(appData.upsideDown && color==flipView)
\r
2961 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2963 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2964 SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2965 if(appData.upsideDown && color==flipView)
\r
2966 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2968 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2970 /* Use square color for details of black pieces */
\r
2971 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2972 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2973 if(appData.upsideDown && !flipView)
\r
2974 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2976 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2978 SelectObject(hdc, oldBrush);
\r
2979 SelectObject(tmphdc, oldBitmap);
\r
2983 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2984 int GetBackTextureMode( int algo )
\r
2986 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2990 case BACK_TEXTURE_MODE_PLAIN:
\r
2991 result = 1; /* Always use identity map */
\r
2993 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2994 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
3002 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
3003 to handle redraws cleanly (as random numbers would always be different).
\r
3005 VOID RebuildTextureSquareInfo()
\r
3015 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
3017 if( liteBackTexture != NULL ) {
\r
3018 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3019 lite_w = bi.bmWidth;
\r
3020 lite_h = bi.bmHeight;
\r
3024 if( darkBackTexture != NULL ) {
\r
3025 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3026 dark_w = bi.bmWidth;
\r
3027 dark_h = bi.bmHeight;
\r
3031 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
3032 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
3033 if( (col + row) & 1 ) {
\r
3035 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
3036 if( lite_w >= squareSize*BOARD_WIDTH )
\r
3037 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
3039 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
3040 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
3041 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
3043 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
3044 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
3049 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
3050 if( dark_w >= squareSize*BOARD_WIDTH )
\r
3051 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
3053 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
3054 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
3055 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
3057 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
3058 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
3065 /* [AS] Arrow highlighting support */
\r
3067 static double A_WIDTH = 5; /* Width of arrow body */
\r
3069 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
3070 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
3072 static double Sqr( double x )
\r
3077 static int Round( double x )
\r
3079 return (int) (x + 0.5);
\r
3082 /* Draw an arrow between two points using current settings */
\r
3083 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
3086 double dx, dy, j, k, x, y;
\r
3088 if( d_x == s_x ) {
\r
3089 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3091 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
3094 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
3095 arrow[1].y = d_y - h;
\r
3097 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3098 arrow[2].y = d_y - h;
\r
3103 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3104 arrow[5].y = d_y - h;
\r
3106 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3107 arrow[4].y = d_y - h;
\r
3109 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3112 else if( d_y == s_y ) {
\r
3113 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3116 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3118 arrow[1].x = d_x - w;
\r
3119 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3121 arrow[2].x = d_x - w;
\r
3122 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3127 arrow[5].x = d_x - w;
\r
3128 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3130 arrow[4].x = d_x - w;
\r
3131 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3134 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3137 /* [AS] Needed a lot of paper for this! :-) */
\r
3138 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3139 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3141 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3143 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3148 arrow[0].x = Round(x - j);
\r
3149 arrow[0].y = Round(y + j*dx);
\r
3151 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3152 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3155 x = (double) d_x - k;
\r
3156 y = (double) d_y - k*dy;
\r
3159 x = (double) d_x + k;
\r
3160 y = (double) d_y + k*dy;
\r
3163 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3165 arrow[6].x = Round(x - j);
\r
3166 arrow[6].y = Round(y + j*dx);
\r
3168 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3169 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3171 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3172 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3177 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3178 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3181 Polygon( hdc, arrow, 7 );
\r
3184 /* [AS] Draw an arrow between two squares */
\r
3185 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3187 int s_x, s_y, d_x, d_y;
\r
3194 if( s_col == d_col && s_row == d_row ) {
\r
3198 /* Get source and destination points */
\r
3199 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3200 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3203 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3205 else if( d_y < s_y ) {
\r
3206 d_y += squareSize / 2 + squareSize / 4;
\r
3209 d_y += squareSize / 2;
\r
3213 d_x += squareSize / 2 - squareSize / 4;
\r
3215 else if( d_x < s_x ) {
\r
3216 d_x += squareSize / 2 + squareSize / 4;
\r
3219 d_x += squareSize / 2;
\r
3222 s_x += squareSize / 2;
\r
3223 s_y += squareSize / 2;
\r
3225 /* Adjust width */
\r
3226 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3229 stLB.lbStyle = BS_SOLID;
\r
3230 stLB.lbColor = appData.highlightArrowColor;
\r
3233 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3234 holdpen = SelectObject( hdc, hpen );
\r
3235 hbrush = CreateBrushIndirect( &stLB );
\r
3236 holdbrush = SelectObject( hdc, hbrush );
\r
3238 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3240 SelectObject( hdc, holdpen );
\r
3241 SelectObject( hdc, holdbrush );
\r
3242 DeleteObject( hpen );
\r
3243 DeleteObject( hbrush );
\r
3246 BOOL HasHighlightInfo()
\r
3248 BOOL result = FALSE;
\r
3250 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3251 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3262 BOOL IsDrawArrowEnabled()
\r
3264 BOOL result = FALSE;
\r
3266 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3273 VOID DrawArrowHighlight( HDC hdc )
\r
3275 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3276 DrawArrowBetweenSquares( hdc,
\r
3277 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3278 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3282 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3284 HRGN result = NULL;
\r
3286 if( HasHighlightInfo() ) {
\r
3287 int x1, y1, x2, y2;
\r
3288 int sx, sy, dx, dy;
\r
3290 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3291 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3293 sx = MIN( x1, x2 );
\r
3294 sy = MIN( y1, y2 );
\r
3295 dx = MAX( x1, x2 ) + squareSize;
\r
3296 dy = MAX( y1, y2 ) + squareSize;
\r
3298 result = CreateRectRgn( sx, sy, dx, dy );
\r
3305 Warning: this function modifies the behavior of several other functions.
\r
3307 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3308 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3309 repaint is scattered all over the place, which is not good for features such as
\r
3310 "arrow highlighting" that require a full repaint of the board.
\r
3312 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3313 user interaction, when speed is not so important) but especially to avoid errors
\r
3314 in the displayed graphics.
\r
3316 In such patched places, I always try refer to this function so there is a single
\r
3317 place to maintain knowledge.
\r
3319 To restore the original behavior, just return FALSE unconditionally.
\r
3321 BOOL IsFullRepaintPreferrable()
\r
3323 BOOL result = FALSE;
\r
3325 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3326 /* Arrow may appear on the board */
\r
3334 This function is called by DrawPosition to know whether a full repaint must
\r
3337 Only DrawPosition may directly call this function, which makes use of
\r
3338 some state information. Other function should call DrawPosition specifying
\r
3339 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3341 BOOL DrawPositionNeedsFullRepaint()
\r
3343 BOOL result = FALSE;
\r
3346 Probably a slightly better policy would be to trigger a full repaint
\r
3347 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3348 but animation is fast enough that it's difficult to notice.
\r
3350 if( animInfo.piece == EmptySquare ) {
\r
3351 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3359 static HBITMAP borderBitmap;
\r
3362 DrawBackgroundOnDC(HDC hdc)
\r
3368 static char oldBorder[MSG_SIZ];
\r
3369 int w = 600, h = 600, mode;
\r
3371 if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid
\r
3372 strncpy(oldBorder, appData.border, MSG_SIZ-1);
\r
3373 borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
3375 if(borderBitmap == NULL) { // loading failed, use white
\r
3376 FillRect( hdc, &boardRect, whitePieceBrush );
\r
3379 tmphdc = CreateCompatibleDC(hdc);
\r
3380 hbm = SelectObject(tmphdc, borderBitmap);
\r
3381 if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {
\r
3385 mode = SetStretchBltMode(hdc, COLORONCOLOR);
\r
3386 StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left,
\r
3387 boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3388 SetStretchBltMode(hdc, mode);
\r
3389 SelectObject(tmphdc, hbm);
\r
3394 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3396 int row, column, x, y, square_color, piece_color;
\r
3397 ChessSquare piece;
\r
3399 HDC texture_hdc = NULL;
\r
3401 /* [AS] Initialize background textures if needed */
\r
3402 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3403 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3404 if( backTextureSquareSize != squareSize
\r
3405 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3406 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3407 backTextureSquareSize = squareSize;
\r
3408 RebuildTextureSquareInfo();
\r
3411 texture_hdc = CreateCompatibleDC( hdc );
\r
3414 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3415 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3417 SquareToPos(row, column, &x, &y);
\r
3419 piece = board[row][column];
\r
3421 square_color = ((column + row) % 2) == 1;
\r
3422 if( gameInfo.variant == VariantXiangqi ) {
\r
3423 square_color = !InPalace(row, column);
\r
3424 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3425 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3427 piece_color = (int) piece < (int) BlackPawn;
\r
3430 /* [HGM] holdings file: light square or black */
\r
3431 if(column == BOARD_LEFT-2) {
\r
3432 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3435 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3439 if(column == BOARD_RGHT + 1 ) {
\r
3440 if( row < gameInfo.holdingsSize )
\r
3443 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3447 if(column == BOARD_LEFT-1 ) /* left align */
\r
3448 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3449 else if( column == BOARD_RGHT) /* right align */
\r
3450 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3451 else if( piece == DarkSquare) DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3453 if (appData.monoMode) {
\r
3454 if (piece == EmptySquare) {
\r
3455 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3456 square_color ? WHITENESS : BLACKNESS);
\r
3458 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3461 else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {
\r
3462 /* [AS] Draw the square using a texture bitmap */
\r
3463 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3464 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3465 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3468 squareSize, squareSize,
\r
3471 backTextureSquareInfo[r][c].mode,
\r
3472 backTextureSquareInfo[r][c].x,
\r
3473 backTextureSquareInfo[r][c].y );
\r
3475 SelectObject( texture_hdc, hbm );
\r
3477 if (piece != EmptySquare) {
\r
3478 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3482 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3484 oldBrush = SelectObject(hdc, brush );
\r
3485 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3486 SelectObject(hdc, oldBrush);
\r
3487 if (piece != EmptySquare)
\r
3488 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3493 if( texture_hdc != NULL ) {
\r
3494 DeleteDC( texture_hdc );
\r
3498 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3499 void fputDW(FILE *f, int x)
\r
3501 fputc(x & 255, f);
\r
3502 fputc(x>>8 & 255, f);
\r
3503 fputc(x>>16 & 255, f);
\r
3504 fputc(x>>24 & 255, f);
\r
3507 #define MAX_CLIPS 200 /* more than enough */
\r
3510 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3512 // HBITMAP bufferBitmap;
\r
3517 int w = 100, h = 50;
\r
3519 if(logo == NULL) {
\r
3520 if(!logoHeight) return;
\r
3521 FillRect( hdc, &logoRect, whitePieceBrush );
\r
3523 // GetClientRect(hwndMain, &Rect);
\r
3524 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3525 // Rect.bottom-Rect.top+1);
\r
3526 tmphdc = CreateCompatibleDC(hdc);
\r
3527 hbm = SelectObject(tmphdc, logo);
\r
3528 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3532 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3533 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3534 SelectObject(tmphdc, hbm);
\r
3542 HDC hdc = GetDC(hwndMain);
\r
3543 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3544 if(appData.autoLogo) {
\r
3546 switch(gameMode) { // pick logos based on game mode
\r
3547 case IcsObserving:
\r
3548 whiteLogo = second.programLogo; // ICS logo
\r
3549 blackLogo = second.programLogo;
\r
3552 case IcsPlayingWhite:
\r
3553 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3554 blackLogo = second.programLogo; // ICS logo
\r
3556 case IcsPlayingBlack:
\r
3557 whiteLogo = second.programLogo; // ICS logo
\r
3558 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3560 case TwoMachinesPlay:
\r
3561 if(first.twoMachinesColor[0] == 'b') {
\r
3562 whiteLogo = second.programLogo;
\r
3563 blackLogo = first.programLogo;
\r
3566 case MachinePlaysWhite:
\r
3567 blackLogo = userLogo;
\r
3569 case MachinePlaysBlack:
\r
3570 whiteLogo = userLogo;
\r
3571 blackLogo = first.programLogo;
\r
3574 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3575 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3576 ReleaseDC(hwndMain, hdc);
\r
3581 UpdateLogos(int display)
\r
3582 { // called after loading new engine(s), in tourney or from menu
\r
3583 LoadLogo(&first, 0, FALSE);
\r
3584 LoadLogo(&second, 1, appData.icsActive);
\r
3585 InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos
\r
3586 if(display) DisplayLogos();
\r
3589 static HDC hdcSeek;
\r
3591 // [HGM] seekgraph
\r
3592 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3595 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3596 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3597 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3598 SelectObject( hdcSeek, hp );
\r
3601 // front-end wrapper for drawing functions to do rectangles
\r
3602 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3607 if (hdcSeek == NULL) {
\r
3608 hdcSeek = GetDC(hwndMain);
\r
3609 if (!appData.monoMode) {
\r
3610 SelectPalette(hdcSeek, hPal, FALSE);
\r
3611 RealizePalette(hdcSeek);
\r
3614 hp = SelectObject( hdcSeek, gridPen );
\r
3615 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3616 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3617 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3618 SelectObject( hdcSeek, hp );
\r
3621 // front-end wrapper for putting text in graph
\r
3622 void DrawSeekText(char *buf, int x, int y)
\r
3625 SetBkMode( hdcSeek, TRANSPARENT );
\r
3626 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3627 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3630 void DrawSeekDot(int x, int y, int color)
\r
3632 int square = color & 0x80;
\r
3633 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3634 color == 0 ? markerBrush[1] : color == 1 ? darkSquareBrush : explodeBrush);
\r
3637 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3638 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3640 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3641 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3642 SelectObject(hdcSeek, oldBrush);
\r
3645 void DrawSeekOpen()
\r
3649 void DrawSeekClose()
\r
3654 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3656 static Board lastReq[2], lastDrawn[2];
\r
3657 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3658 static int lastDrawnFlipView = 0;
\r
3659 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3660 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3663 HBITMAP bufferBitmap;
\r
3664 HBITMAP oldBitmap;
\r
3666 HRGN clips[MAX_CLIPS];
\r
3667 ChessSquare dragged_piece = EmptySquare;
\r
3668 int nr = twoBoards*partnerUp;
\r
3670 /* I'm undecided on this - this function figures out whether a full
\r
3671 * repaint is necessary on its own, so there's no real reason to have the
\r
3672 * caller tell it that. I think this can safely be set to FALSE - but
\r
3673 * if we trust the callers not to request full repaints unnessesarily, then
\r
3674 * we could skip some clipping work. In other words, only request a full
\r
3675 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3676 * gamestart and similar) --Hawk
\r
3678 Boolean fullrepaint = repaint;
\r
3680 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3682 if( DrawPositionNeedsFullRepaint() ) {
\r
3683 fullrepaint = TRUE;
\r
3686 if (board == NULL) {
\r
3687 if (!lastReqValid[nr]) {
\r
3690 board = lastReq[nr];
\r
3692 CopyBoard(lastReq[nr], board);
\r
3693 lastReqValid[nr] = 1;
\r
3696 if (doingSizing) {
\r
3700 if (IsIconic(hwndMain)) {
\r
3704 if (hdc == NULL) {
\r
3705 hdc = GetDC(hwndMain);
\r
3706 if (!appData.monoMode) {
\r
3707 SelectPalette(hdc, hPal, FALSE);
\r
3708 RealizePalette(hdc);
\r
3712 releaseDC = FALSE;
\r
3715 /* Create some work-DCs */
\r
3716 hdcmem = CreateCompatibleDC(hdc);
\r
3717 tmphdc = CreateCompatibleDC(hdc);
\r
3719 /* If dragging is in progress, we temporarely remove the piece */
\r
3720 /* [HGM] or temporarily decrease count if stacked */
\r
3721 /* !! Moved to before board compare !! */
\r
3722 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3723 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3724 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3725 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3726 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3728 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3729 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3730 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3732 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3735 /* Figure out which squares need updating by comparing the
\r
3736 * newest board with the last drawn board and checking if
\r
3737 * flipping has changed.
\r
3739 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3740 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3741 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3742 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3743 SquareToPos(row, column, &x, &y);
\r
3744 clips[num_clips++] =
\r
3745 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3749 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3750 for (i=0; i<2; i++) {
\r
3751 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3752 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3753 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3754 lastDrawnHighlight.sq[i].y >= 0) {
\r
3755 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3756 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3757 clips[num_clips++] =
\r
3758 CreateRectRgn(x - lineGap, y - lineGap,
\r
3759 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3761 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3762 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3763 clips[num_clips++] =
\r
3764 CreateRectRgn(x - lineGap, y - lineGap,
\r
3765 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3769 for (i=0; i<2; i++) {
\r
3770 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3771 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3772 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3773 lastDrawnPremove.sq[i].y >= 0) {
\r
3774 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3775 lastDrawnPremove.sq[i].x, &x, &y);
\r
3776 clips[num_clips++] =
\r
3777 CreateRectRgn(x - lineGap, y - lineGap,
\r
3778 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3780 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3781 premoveHighlightInfo.sq[i].y >= 0) {
\r
3782 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3783 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3784 clips[num_clips++] =
\r
3785 CreateRectRgn(x - lineGap, y - lineGap,
\r
3786 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3790 } else { // nr == 1
\r
3791 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3792 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3793 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3794 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3795 for (i=0; i<2; i++) {
\r
3796 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3797 partnerHighlightInfo.sq[i].y >= 0) {
\r
3798 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3799 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3800 clips[num_clips++] =
\r
3801 CreateRectRgn(x - lineGap, y - lineGap,
\r
3802 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3804 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3805 oldPartnerHighlight.sq[i].y >= 0) {
\r
3806 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3807 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3808 clips[num_clips++] =
\r
3809 CreateRectRgn(x - lineGap, y - lineGap,
\r
3810 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3815 fullrepaint = TRUE;
\r
3818 /* Create a buffer bitmap - this is the actual bitmap
\r
3819 * being written to. When all the work is done, we can
\r
3820 * copy it to the real DC (the screen). This avoids
\r
3821 * the problems with flickering.
\r
3823 GetClientRect(hwndMain, &Rect);
\r
3824 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3825 Rect.bottom-Rect.top+1);
\r
3826 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3827 if (!appData.monoMode) {
\r
3828 SelectPalette(hdcmem, hPal, FALSE);
\r
3831 /* Create clips for dragging */
\r
3832 if (!fullrepaint) {
\r
3833 if (dragInfo.from.x >= 0) {
\r
3834 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3835 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3837 if (dragInfo.start.x >= 0) {
\r
3838 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3839 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3841 if (dragInfo.pos.x >= 0) {
\r
3842 x = dragInfo.pos.x - squareSize / 2;
\r
3843 y = dragInfo.pos.y - squareSize / 2;
\r
3844 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3846 if (dragInfo.lastpos.x >= 0) {
\r
3847 x = dragInfo.lastpos.x - squareSize / 2;
\r
3848 y = dragInfo.lastpos.y - squareSize / 2;
\r
3849 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3853 /* Are we animating a move?
\r
3855 * - remove the piece from the board (temporarely)
\r
3856 * - calculate the clipping region
\r
3858 if (!fullrepaint) {
\r
3859 if (animInfo.piece != EmptySquare) {
\r
3860 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3861 x = boardRect.left + animInfo.lastpos.x;
\r
3862 y = boardRect.top + animInfo.lastpos.y;
\r
3863 x2 = boardRect.left + animInfo.pos.x;
\r
3864 y2 = boardRect.top + animInfo.pos.y;
\r
3865 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3866 /* Slight kludge. The real problem is that after AnimateMove is
\r
3867 done, the position on the screen does not match lastDrawn.
\r
3868 This currently causes trouble only on e.p. captures in
\r
3869 atomic, where the piece moves to an empty square and then
\r
3870 explodes. The old and new positions both had an empty square
\r
3871 at the destination, but animation has drawn a piece there and
\r
3872 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3873 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3877 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3878 if (num_clips == 0)
\r
3879 fullrepaint = TRUE;
\r
3881 /* Set clipping on the memory DC */
\r
3882 if (!fullrepaint) {
\r
3883 SelectClipRgn(hdcmem, clips[0]);
\r
3884 for (x = 1; x < num_clips; x++) {
\r
3885 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3886 abort(); // this should never ever happen!
\r
3890 /* Do all the drawing to the memory DC */
\r
3891 if(explodeInfo.radius) { // [HGM] atomic
\r
3893 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3894 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3895 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3896 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3897 x += squareSize/2;
\r
3898 y += squareSize/2;
\r
3899 if(!fullrepaint) {
\r
3900 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3901 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3903 DrawGridOnDC(hdcmem);
\r
3904 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3905 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3906 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3907 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3908 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3909 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3910 SelectObject(hdcmem, oldBrush);
\r
3912 if(border) DrawBackgroundOnDC(hdcmem);
\r
3913 DrawGridOnDC(hdcmem);
\r
3914 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3915 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3916 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3918 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3919 oldPartnerHighlight = partnerHighlightInfo;
\r
3921 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3923 if(nr == 0) // [HGM] dual: markers only on left board
\r
3924 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3925 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3926 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3927 HBRUSH oldBrush = SelectObject(hdcmem, markerBrush[marker[row][column]-1]);
\r
3928 SquareToPos(row, column, &x, &y);
\r
3929 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3930 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3931 SelectObject(hdcmem, oldBrush);
\r
3936 if( appData.highlightMoveWithArrow ) {
\r
3937 DrawArrowHighlight(hdcmem);
\r
3940 DrawCoordsOnDC(hdcmem);
\r
3942 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3943 /* to make sure lastDrawn contains what is actually drawn */
\r
3945 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3946 if (dragged_piece != EmptySquare) {
\r
3947 /* [HGM] or restack */
\r
3948 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3949 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3951 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3952 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3953 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3954 x = dragInfo.pos.x - squareSize / 2;
\r
3955 y = dragInfo.pos.y - squareSize / 2;
\r
3956 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
3957 ((int) dragInfo.piece < (int) BlackPawn),
\r
3958 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3961 /* Put the animated piece back into place and draw it */
\r
3962 if (animInfo.piece != EmptySquare) {
\r
3963 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3964 x = boardRect.left + animInfo.pos.x;
\r
3965 y = boardRect.top + animInfo.pos.y;
\r
3966 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3967 ((int) animInfo.piece < (int) BlackPawn),
\r
3968 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3971 /* Release the bufferBitmap by selecting in the old bitmap
\r
3972 * and delete the memory DC
\r
3974 SelectObject(hdcmem, oldBitmap);
\r
3977 /* Set clipping on the target DC */
\r
3978 if (!fullrepaint) {
\r
3979 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3981 GetRgnBox(clips[x], &rect);
\r
3982 DeleteObject(clips[x]);
\r
3983 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3984 rect.right + wpMain.width/2, rect.bottom);
\r
3986 SelectClipRgn(hdc, clips[0]);
\r
3987 for (x = 1; x < num_clips; x++) {
\r
3988 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3989 abort(); // this should never ever happen!
\r
3993 /* Copy the new bitmap onto the screen in one go.
\r
3994 * This way we avoid any flickering
\r
3996 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3997 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3998 boardRect.right - boardRect.left,
\r
3999 boardRect.bottom - boardRect.top,
\r
4000 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
4001 if(saveDiagFlag) {
\r
4002 BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData;
\r
4003 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
4005 GetObject(bufferBitmap, sizeof(b), &b);
\r
4006 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\r
4007 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
4008 bih.biWidth = b.bmWidth;
\r
4009 bih.biHeight = b.bmHeight;
\r
4011 bih.biBitCount = b.bmBitsPixel;
\r
4012 bih.biCompression = 0;
\r
4013 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
4014 bih.biXPelsPerMeter = 0;
\r
4015 bih.biYPelsPerMeter = 0;
\r
4016 bih.biClrUsed = 0;
\r
4017 bih.biClrImportant = 0;
\r
4018 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
4019 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
4020 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
4021 // fprintf(diagFile, "%8x\n", (int) pData);
\r
4023 wb = b.bmWidthBytes;
\r
4025 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
4026 int k = ((int*) pData)[i];
\r
4027 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4028 if(j >= 16) break;
\r
4030 if(j >= nrColors) nrColors = j+1;
\r
4032 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
4034 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
4035 for(w=0; w<(wb>>2); w+=2) {
\r
4036 int k = ((int*) pData)[(wb*i>>2) + w];
\r
4037 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4038 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
4039 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
4040 pData[p++] = m | j<<4;
\r
4042 while(p&3) pData[p++] = 0;
\r
4045 wb = ((wb+31)>>5)<<2;
\r
4047 // write BITMAPFILEHEADER
\r
4048 fprintf(diagFile, "BM");
\r
4049 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
4050 fputDW(diagFile, 0);
\r
4051 fputDW(diagFile, 0x36 + (fac?64:0));
\r
4052 // write BITMAPINFOHEADER
\r
4053 fputDW(diagFile, 40);
\r
4054 fputDW(diagFile, b.bmWidth);
\r
4055 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
4056 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
4057 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
4058 fputDW(diagFile, 0);
\r
4059 fputDW(diagFile, 0);
\r
4060 fputDW(diagFile, 0);
\r
4061 fputDW(diagFile, 0);
\r
4062 fputDW(diagFile, 0);
\r
4063 fputDW(diagFile, 0);
\r
4064 // write color table
\r
4066 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
4067 // write bitmap data
\r
4068 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
4069 fputc(pData[i], diagFile);
\r
4074 SelectObject(tmphdc, oldBitmap);
\r
4076 /* Massive cleanup */
\r
4077 for (x = 0; x < num_clips; x++)
\r
4078 DeleteObject(clips[x]);
\r
4081 DeleteObject(bufferBitmap);
\r
4084 ReleaseDC(hwndMain, hdc);
\r
4086 if (lastDrawnFlipView != flipView && nr == 0) {
\r
4088 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
4090 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
4093 /* CopyBoard(lastDrawn, board);*/
\r
4094 lastDrawnHighlight = highlightInfo;
\r
4095 lastDrawnPremove = premoveHighlightInfo;
\r
4096 lastDrawnFlipView = flipView;
\r
4097 lastDrawnValid[nr] = 1;
\r
4100 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
4105 saveDiagFlag = 1; diagFile = f;
\r
4106 HDCDrawPosition(NULL, TRUE, NULL);
\r
4114 /*---------------------------------------------------------------------------*\
\r
4115 | CLIENT PAINT PROCEDURE
\r
4116 | This is the main event-handler for the WM_PAINT message.
\r
4118 \*---------------------------------------------------------------------------*/
\r
4120 PaintProc(HWND hwnd)
\r
4126 if((hdc = BeginPaint(hwnd, &ps))) {
\r
4127 if (IsIconic(hwnd)) {
\r
4128 DrawIcon(hdc, 2, 2, iconCurrent);
\r
4130 if (!appData.monoMode) {
\r
4131 SelectPalette(hdc, hPal, FALSE);
\r
4132 RealizePalette(hdc);
\r
4134 HDCDrawPosition(hdc, 1, NULL);
\r
4135 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
4136 flipView = !flipView; partnerUp = !partnerUp;
\r
4137 HDCDrawPosition(hdc, 1, NULL);
\r
4138 flipView = !flipView; partnerUp = !partnerUp;
\r
4141 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
4142 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
4143 ETO_CLIPPED|ETO_OPAQUE,
\r
4144 &messageRect, messageText, strlen(messageText), NULL);
\r
4145 SelectObject(hdc, oldFont);
\r
4146 DisplayBothClocks();
\r
4149 EndPaint(hwnd,&ps);
\r
4157 * If the user selects on a border boundary, return -1; if off the board,
\r
4158 * return -2. Otherwise map the event coordinate to the square.
\r
4159 * The offset boardRect.left or boardRect.top must already have been
\r
4160 * subtracted from x.
\r
4162 int EventToSquare(x, limit)
\r
4167 if (x < lineGap + border)
\r
4169 x -= lineGap + border;
\r
4170 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4172 x /= (squareSize + lineGap);
\r
4184 DropEnable dropEnables[] = {
\r
4185 { 'P', DP_Pawn, N_("Pawn") },
\r
4186 { 'N', DP_Knight, N_("Knight") },
\r
4187 { 'B', DP_Bishop, N_("Bishop") },
\r
4188 { 'R', DP_Rook, N_("Rook") },
\r
4189 { 'Q', DP_Queen, N_("Queen") },
\r
4193 SetupDropMenu(HMENU hmenu)
\r
4195 int i, count, enable;
\r
4197 extern char white_holding[], black_holding[];
\r
4198 char item[MSG_SIZ];
\r
4200 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4201 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4202 dropEnables[i].piece);
\r
4204 while (p && *p++ == dropEnables[i].piece) count++;
\r
4205 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4206 enable = count > 0 || !appData.testLegality
\r
4207 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4208 && !appData.icsActive);
\r
4209 ModifyMenu(hmenu, dropEnables[i].command,
\r
4210 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4211 dropEnables[i].command, item);
\r
4215 void DragPieceBegin(int x, int y, Boolean instantly)
\r
4217 dragInfo.lastpos.x = boardRect.left + x;
\r
4218 dragInfo.lastpos.y = boardRect.top + y;
\r
4219 if(instantly) dragInfo.pos = dragInfo.lastpos;
\r
4220 dragInfo.from.x = fromX;
\r
4221 dragInfo.from.y = fromY;
\r
4222 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4223 dragInfo.start = dragInfo.from;
\r
4224 SetCapture(hwndMain);
\r
4227 void DragPieceEnd(int x, int y)
\r
4230 dragInfo.start.x = dragInfo.start.y = -1;
\r
4231 dragInfo.from = dragInfo.start;
\r
4232 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4235 void ChangeDragPiece(ChessSquare piece)
\r
4237 dragInfo.piece = piece;
\r
4240 /* Event handler for mouse messages */
\r
4242 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4246 static int recursive = 0;
\r
4248 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4251 if (message == WM_MBUTTONUP) {
\r
4252 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4253 to the middle button: we simulate pressing the left button too!
\r
4255 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4256 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4262 pt.x = LOWORD(lParam);
\r
4263 pt.y = HIWORD(lParam);
\r
4264 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4265 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4266 if (!flipView && y >= 0) {
\r
4267 y = BOARD_HEIGHT - 1 - y;
\r
4269 if (flipView && x >= 0) {
\r
4270 x = BOARD_WIDTH - 1 - x;
\r
4273 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4274 controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status
\r
4276 switch (message) {
\r
4277 case WM_LBUTTONDOWN:
\r
4278 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4279 ClockClick(flipClock); break;
\r
4280 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4281 ClockClick(!flipClock); break;
\r
4283 if(dragging) { // [HGM] lion: don't destroy dragging info if we are already dragging
\r
4284 dragInfo.start.x = dragInfo.start.y = -1;
\r
4285 dragInfo.from = dragInfo.start;
\r
4287 if(fromX == -1 && frozen) { // not sure where this is for
\r
4288 fromX = fromY = -1;
\r
4289 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4292 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4293 DrawPosition(TRUE, NULL);
\r
4296 case WM_LBUTTONUP:
\r
4297 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4298 DrawPosition(TRUE, NULL);
\r
4301 case WM_MOUSEMOVE:
\r
4302 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4303 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4304 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4305 if ((appData.animateDragging || appData.highlightDragging)
\r
4306 && (wParam & MK_LBUTTON || dragging == 2)
\r
4307 && dragInfo.from.x >= 0)
\r
4309 BOOL full_repaint = FALSE;
\r
4311 if (appData.animateDragging) {
\r
4312 dragInfo.pos = pt;
\r
4314 if (appData.highlightDragging) {
\r
4315 HoverEvent(highlightInfo.sq[1].x, highlightInfo.sq[1].y, x, y);
\r
4316 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4317 full_repaint = TRUE;
\r
4321 DrawPosition( full_repaint, NULL);
\r
4323 dragInfo.lastpos = dragInfo.pos;
\r
4327 case WM_MOUSEWHEEL: // [DM]
\r
4328 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4329 /* Mouse Wheel is being rolled forward
\r
4330 * Play moves forward
\r
4332 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4333 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4334 /* Mouse Wheel is being rolled backward
\r
4335 * Play moves backward
\r
4337 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4338 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4342 case WM_MBUTTONUP:
\r
4343 case WM_RBUTTONUP:
\r
4345 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4348 case WM_MBUTTONDOWN:
\r
4349 case WM_RBUTTONDOWN:
\r
4352 fromX = fromY = -1;
\r
4353 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4354 dragInfo.start.x = dragInfo.start.y = -1;
\r
4355 dragInfo.from = dragInfo.start;
\r
4356 dragInfo.lastpos = dragInfo.pos;
\r
4357 if (appData.highlightDragging) {
\r
4358 ClearHighlights();
\r
4361 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4362 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4363 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4364 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4365 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4369 DrawPosition(TRUE, NULL);
\r
4371 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4374 if (message == WM_MBUTTONDOWN) {
\r
4375 buttonCount = 3; /* even if system didn't think so */
\r
4376 if (wParam & MK_SHIFT)
\r
4377 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4379 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4380 } else { /* message == WM_RBUTTONDOWN */
\r
4381 /* Just have one menu, on the right button. Windows users don't
\r
4382 think to try the middle one, and sometimes other software steals
\r
4383 it, or it doesn't really exist. */
\r
4384 if(gameInfo.variant != VariantShogi)
\r
4385 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4387 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4391 SetCapture(hwndMain);
\r
4394 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4395 SetupDropMenu(hmenu);
\r
4396 MenuPopup(hwnd, pt, hmenu, -1);
\r
4406 /* Preprocess messages for buttons in main window */
\r
4408 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4410 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4413 for (i=0; i<N_BUTTONS; i++) {
\r
4414 if (buttonDesc[i].id == id) break;
\r
4416 if (i == N_BUTTONS) return 0;
\r
4417 switch (message) {
\r
4422 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4423 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4430 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4433 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4434 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4435 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4436 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4438 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4440 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4441 TypeInEvent((char)wParam);
\r
4447 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4450 static int promoStyle;
\r
4452 /* Process messages for Promotion dialog box */
\r
4454 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4458 switch (message) {
\r
4459 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4460 /* Center the dialog over the application window */
\r
4461 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4462 Translate(hDlg, DLG_PromotionKing);
\r
4463 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4464 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4465 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4466 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4467 SW_SHOW : SW_HIDE);
\r
4468 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4469 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4470 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4471 PieceToChar(WhiteAngel) != '~') ||
\r
4472 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4473 PieceToChar(BlackAngel) != '~') ) ?
\r
4474 SW_SHOW : SW_HIDE);
\r
4475 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4476 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4477 PieceToChar(WhiteMarshall) != '~') ||
\r
4478 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4479 PieceToChar(BlackMarshall) != '~') ) ?
\r
4480 SW_SHOW : SW_HIDE);
\r
4481 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4482 ShowWindow(GetDlgItem(hDlg, PB_Rook), !promoStyle ? SW_SHOW : SW_HIDE);
\r
4483 ShowWindow(GetDlgItem(hDlg, PB_Bishop), !promoStyle ? SW_SHOW : SW_HIDE);
\r
4485 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4486 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4487 SetWindowText(hDlg, "Promote?");
\r
4489 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4490 gameInfo.variant == VariantSuper ?
\r
4491 SW_SHOW : SW_HIDE);
\r
4494 case WM_COMMAND: /* message: received a command */
\r
4495 switch (LOWORD(wParam)) {
\r
4497 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4498 ClearHighlights();
\r
4499 DrawPosition(FALSE, NULL);
\r
4502 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4505 promoChar = promoStyle ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4508 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4509 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4512 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4513 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4515 case PB_Chancellor:
\r
4516 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4518 case PB_Archbishop:
\r
4519 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4522 promoChar = gameInfo.variant == VariantShogi ? '=' : promoStyle ? NULLCHAR :
\r
4523 ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight));
\r
4528 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4529 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4530 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4531 fromX = fromY = -1;
\r
4532 if (!appData.highlightLastMove) {
\r
4533 ClearHighlights();
\r
4534 DrawPosition(FALSE, NULL);
\r
4541 /* Pop up promotion dialog */
\r
4543 PromotionPopup(HWND hwnd)
\r
4547 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4548 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4549 hwnd, (DLGPROC)lpProc);
\r
4550 FreeProcInstance(lpProc);
\r
4554 PromotionPopUp(char choice)
\r
4556 promoStyle = (choice == '+');
\r
4557 DrawPosition(TRUE, NULL);
\r
4558 PromotionPopup(hwndMain);
\r
4562 LoadGameDialog(HWND hwnd, char* title)
\r
4566 char fileTitle[MSG_SIZ];
\r
4567 f = OpenFileDialog(hwnd, "rb", "",
\r
4568 appData.oldSaveStyle ? "gam" : "pgn",
\r
4570 title, &number, fileTitle, NULL);
\r
4572 cmailMsgLoaded = FALSE;
\r
4573 if (number == 0) {
\r
4574 int error = GameListBuild(f);
\r
4576 DisplayError(_("Cannot build game list"), error);
\r
4577 } else if (!ListEmpty(&gameList) &&
\r
4578 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4579 GameListPopUp(f, fileTitle);
\r
4582 GameListDestroy();
\r
4585 LoadGame(f, number, fileTitle, FALSE);
\r
4589 int get_term_width()
\r
4594 HFONT hfont, hold_font;
\r
4599 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4603 // get the text metrics
\r
4604 hdc = GetDC(hText);
\r
4605 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4606 if (consoleCF.dwEffects & CFE_BOLD)
\r
4607 lf.lfWeight = FW_BOLD;
\r
4608 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4609 lf.lfItalic = TRUE;
\r
4610 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4611 lf.lfStrikeOut = TRUE;
\r
4612 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4613 lf.lfUnderline = TRUE;
\r
4614 hfont = CreateFontIndirect(&lf);
\r
4615 hold_font = SelectObject(hdc, hfont);
\r
4616 GetTextMetrics(hdc, &tm);
\r
4617 SelectObject(hdc, hold_font);
\r
4618 DeleteObject(hfont);
\r
4619 ReleaseDC(hText, hdc);
\r
4621 // get the rectangle
\r
4622 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4624 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4627 void UpdateICSWidth(HWND hText)
\r
4629 LONG old_width, new_width;
\r
4631 new_width = get_term_width(hText, FALSE);
\r
4632 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4633 if (new_width != old_width)
\r
4635 ics_update_width(new_width);
\r
4636 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4641 ChangedConsoleFont()
\r
4644 CHARRANGE tmpsel, sel;
\r
4645 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4646 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4647 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4650 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4651 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4652 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4653 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4654 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4655 * size. This was undocumented in the version of MSVC++ that I had
\r
4656 * when I wrote the code, but is apparently documented now.
\r
4658 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4659 cfmt.bCharSet = f->lf.lfCharSet;
\r
4660 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4661 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4662 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4663 /* Why are the following seemingly needed too? */
\r
4664 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4665 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4666 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4668 tmpsel.cpMax = -1; /*999999?*/
\r
4669 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4670 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4671 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4672 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4674 paraf.cbSize = sizeof(paraf);
\r
4675 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4676 paraf.dxStartIndent = 0;
\r
4677 paraf.dxOffset = WRAP_INDENT;
\r
4678 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4679 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4680 UpdateICSWidth(hText);
\r
4683 /*---------------------------------------------------------------------------*\
\r
4685 * Window Proc for main window
\r
4687 \*---------------------------------------------------------------------------*/
\r
4689 /* Process messages for main window, etc. */
\r
4691 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4694 int wmId, wmEvent;
\r
4698 char fileTitle[MSG_SIZ];
\r
4699 static SnapData sd;
\r
4700 static int peek=0;
\r
4702 switch (message) {
\r
4704 case WM_PAINT: /* message: repaint portion of window */
\r
4708 case WM_ERASEBKGND:
\r
4709 if (IsIconic(hwnd)) {
\r
4710 /* Cheat; change the message */
\r
4711 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4713 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4717 case WM_LBUTTONDOWN:
\r
4718 case WM_MBUTTONDOWN:
\r
4719 case WM_RBUTTONDOWN:
\r
4720 case WM_LBUTTONUP:
\r
4721 case WM_MBUTTONUP:
\r
4722 case WM_RBUTTONUP:
\r
4723 case WM_MOUSEMOVE:
\r
4724 case WM_MOUSEWHEEL:
\r
4725 MouseEvent(hwnd, message, wParam, lParam);
\r
4729 if((char)wParam == '\b') {
\r
4730 ForwardEvent(); peek = 0;
\r
4733 JAWS_KBUP_NAVIGATION
\r
4738 if((char)wParam == '\b') {
\r
4739 if(!peek) BackwardEvent(), peek = 1;
\r
4742 JAWS_KBDOWN_NAVIGATION
\r
4748 JAWS_ALT_INTERCEPT
\r
4750 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4751 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4752 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4753 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4755 SendMessage(h, message, wParam, lParam);
\r
4756 } else if(lParam != KF_REPEAT) {
\r
4757 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4758 TypeInEvent((char)wParam);
\r
4759 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4760 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4765 case WM_PALETTECHANGED:
\r
4766 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4768 HDC hdc = GetDC(hwndMain);
\r
4769 SelectPalette(hdc, hPal, TRUE);
\r
4770 nnew = RealizePalette(hdc);
\r
4772 paletteChanged = TRUE;
\r
4774 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4776 ReleaseDC(hwnd, hdc);
\r
4780 case WM_QUERYNEWPALETTE:
\r
4781 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4783 HDC hdc = GetDC(hwndMain);
\r
4784 paletteChanged = FALSE;
\r
4785 SelectPalette(hdc, hPal, FALSE);
\r
4786 nnew = RealizePalette(hdc);
\r
4788 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4790 ReleaseDC(hwnd, hdc);
\r
4795 case WM_COMMAND: /* message: command from application menu */
\r
4796 wmId = LOWORD(wParam);
\r
4797 wmEvent = HIWORD(wParam);
\r
4802 SAY("new game enter a move to play against the computer with white");
\r
4805 case IDM_NewGameFRC:
\r
4806 if( NewGameFRC() == 0 ) {
\r
4811 case IDM_NewVariant:
\r
4812 NewVariantPopup(hwnd);
\r
4815 case IDM_LoadGame:
\r
4816 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4819 case IDM_LoadNextGame:
\r
4823 case IDM_LoadPrevGame:
\r
4827 case IDM_ReloadGame:
\r
4831 case IDM_LoadPosition:
\r
4832 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4833 Reset(FALSE, TRUE);
\r
4836 f = OpenFileDialog(hwnd, "rb", "",
\r
4837 appData.oldSaveStyle ? "pos" : "fen",
\r
4839 _("Load Position from File"), &number, fileTitle, NULL);
\r
4841 LoadPosition(f, number, fileTitle);
\r
4845 case IDM_LoadNextPosition:
\r
4846 ReloadPosition(1);
\r
4849 case IDM_LoadPrevPosition:
\r
4850 ReloadPosition(-1);
\r
4853 case IDM_ReloadPosition:
\r
4854 ReloadPosition(0);
\r
4857 case IDM_SaveGame:
\r
4858 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4859 f = OpenFileDialog(hwnd, "a", defName,
\r
4860 appData.oldSaveStyle ? "gam" : "pgn",
\r
4862 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4864 SaveGame(f, 0, "");
\r
4868 case IDM_SavePosition:
\r
4869 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4870 f = OpenFileDialog(hwnd, "a", defName,
\r
4871 appData.oldSaveStyle ? "pos" : "fen",
\r
4873 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4875 SavePosition(f, 0, "");
\r
4879 case IDM_SaveDiagram:
\r
4880 defName = "diagram";
\r
4881 f = OpenFileDialog(hwnd, "wb", defName,
\r
4884 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
4890 case IDM_CreateBook:
\r
4891 CreateBookEvent();
\r
4894 case IDM_CopyGame:
\r
4895 CopyGameToClipboard();
\r
4898 case IDM_PasteGame:
\r
4899 PasteGameFromClipboard();
\r
4902 case IDM_CopyGameListToClipboard:
\r
4903 CopyGameListToClipboard();
\r
4906 /* [AS] Autodetect FEN or PGN data */
\r
4907 case IDM_PasteAny:
\r
4908 PasteGameOrFENFromClipboard();
\r
4911 /* [AS] Move history */
\r
4912 case IDM_ShowMoveHistory:
\r
4913 if( MoveHistoryIsUp() ) {
\r
4914 MoveHistoryPopDown();
\r
4917 MoveHistoryPopUp();
\r
4921 /* [AS] Eval graph */
\r
4922 case IDM_ShowEvalGraph:
\r
4923 if( EvalGraphIsUp() ) {
\r
4924 EvalGraphPopDown();
\r
4928 SetFocus(hwndMain);
\r
4932 /* [AS] Engine output */
\r
4933 case IDM_ShowEngineOutput:
\r
4934 if( EngineOutputIsUp() ) {
\r
4935 EngineOutputPopDown();
\r
4938 EngineOutputPopUp();
\r
4942 /* [AS] User adjudication */
\r
4943 case IDM_UserAdjudication_White:
\r
4944 UserAdjudicationEvent( +1 );
\r
4947 case IDM_UserAdjudication_Black:
\r
4948 UserAdjudicationEvent( -1 );
\r
4951 case IDM_UserAdjudication_Draw:
\r
4952 UserAdjudicationEvent( 0 );
\r
4955 /* [AS] Game list options dialog */
\r
4956 case IDM_GameListOptions:
\r
4957 GameListOptions();
\r
4964 case IDM_CopyPosition:
\r
4965 CopyFENToClipboard();
\r
4968 case IDM_PastePosition:
\r
4969 PasteFENFromClipboard();
\r
4972 case IDM_MailMove:
\r
4976 case IDM_ReloadCMailMsg:
\r
4977 Reset(TRUE, TRUE);
\r
4978 ReloadCmailMsgEvent(FALSE);
\r
4981 case IDM_Minimize:
\r
4982 ShowWindow(hwnd, SW_MINIMIZE);
\r
4989 case IDM_MachineWhite:
\r
4990 MachineWhiteEvent();
\r
4992 * refresh the tags dialog only if it's visible
\r
4994 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4996 tags = PGNTags(&gameInfo);
\r
4997 TagsPopUp(tags, CmailMsg());
\r
5000 SAY("computer starts playing white");
\r
5003 case IDM_MachineBlack:
\r
5004 MachineBlackEvent();
\r
5006 * refresh the tags dialog only if it's visible
\r
5008 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
5010 tags = PGNTags(&gameInfo);
\r
5011 TagsPopUp(tags, CmailMsg());
\r
5014 SAY("computer starts playing black");
\r
5017 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
5018 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
5021 case IDM_TwoMachines:
\r
5022 TwoMachinesEvent();
\r
5024 * refresh the tags dialog only if it's visible
\r
5026 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
5028 tags = PGNTags(&gameInfo);
\r
5029 TagsPopUp(tags, CmailMsg());
\r
5032 SAY("computer starts playing both sides");
\r
5035 case IDM_AnalysisMode:
\r
5036 if(AnalyzeModeEvent()) {
\r
5037 SAY("analyzing current position");
\r
5041 case IDM_AnalyzeFile:
\r
5042 AnalyzeFileEvent();
\r
5045 case IDM_IcsClient:
\r
5049 case IDM_EditGame:
\r
5050 case IDM_EditGame2:
\r
5055 case IDM_EditPosition:
\r
5056 case IDM_EditPosition2:
\r
5057 EditPositionEvent();
\r
5058 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
5061 case IDM_Training:
\r
5065 case IDM_ShowGameList:
\r
5066 ShowGameListProc();
\r
5069 case IDM_EditProgs1:
\r
5070 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
5073 case IDM_LoadProg1:
\r
5074 LoadEnginePopUp(hwndMain, 0);
\r
5077 case IDM_LoadProg2:
\r
5078 LoadEnginePopUp(hwndMain, 1);
\r
5081 case IDM_EditServers:
\r
5082 EditTagsPopUp(icsNames, &icsNames);
\r
5085 case IDM_EditTags:
\r
5090 case IDM_EditBook:
\r
5094 case IDM_EditComment:
\r
5096 if (commentUp && editComment) {
\r
5099 EditCommentEvent();
\r
5120 case IDM_CallFlag:
\r
5140 case IDM_StopObserving:
\r
5141 StopObservingEvent();
\r
5144 case IDM_StopExamining:
\r
5145 StopExaminingEvent();
\r
5149 UploadGameEvent();
\r
5152 case IDM_TypeInMove:
\r
5153 TypeInEvent('\000');
\r
5156 case IDM_TypeInName:
\r
5157 PopUpNameDialog('\000');
\r
5160 case IDM_Backward:
\r
5162 SetFocus(hwndMain);
\r
5169 SetFocus(hwndMain);
\r
5174 SetFocus(hwndMain);
\r
5179 SetFocus(hwndMain);
\r
5182 case OPT_GameListNext: // [HGM] forward these two accelerators to Game List
\r
5183 case OPT_GameListPrev:
\r
5184 if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);
\r
5188 RevertEvent(FALSE);
\r
5191 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5192 RevertEvent(TRUE);
\r
5195 case IDM_TruncateGame:
\r
5196 TruncateGameEvent();
\r
5203 case IDM_RetractMove:
\r
5204 RetractMoveEvent();
\r
5207 case IDM_FlipView:
\r
5208 flipView = !flipView;
\r
5209 DrawPosition(FALSE, NULL);
\r
5212 case IDM_FlipClock:
\r
5213 flipClock = !flipClock;
\r
5214 DisplayBothClocks();
\r
5218 case IDM_MuteSounds:
\r
5219 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5220 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5221 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5224 case IDM_GeneralOptions:
\r
5225 GeneralOptionsPopup(hwnd);
\r
5226 DrawPosition(TRUE, NULL);
\r
5229 case IDM_BoardOptions:
\r
5230 BoardOptionsPopup(hwnd);
\r
5233 case IDM_ThemeOptions:
\r
5234 ThemeOptionsPopup(hwnd);
\r
5237 case IDM_EnginePlayOptions:
\r
5238 EnginePlayOptionsPopup(hwnd);
\r
5241 case IDM_Engine1Options:
\r
5242 EngineOptionsPopup(hwnd, &first);
\r
5245 case IDM_Engine2Options:
\r
5247 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5248 EngineOptionsPopup(hwnd, &second);
\r
5251 case IDM_OptionsUCI:
\r
5252 UciOptionsPopup(hwnd);
\r
5256 TourneyPopup(hwnd);
\r
5259 case IDM_IcsOptions:
\r
5260 IcsOptionsPopup(hwnd);
\r
5264 FontsOptionsPopup(hwnd);
\r
5268 SoundOptionsPopup(hwnd);
\r
5271 case IDM_CommPort:
\r
5272 CommPortOptionsPopup(hwnd);
\r
5275 case IDM_LoadOptions:
\r
5276 LoadOptionsPopup(hwnd);
\r
5279 case IDM_SaveOptions:
\r
5280 SaveOptionsPopup(hwnd);
\r
5283 case IDM_TimeControl:
\r
5284 TimeControlOptionsPopup(hwnd);
\r
5287 case IDM_SaveSettings:
\r
5288 SaveSettings(settingsFileName);
\r
5291 case IDM_SaveSettingsOnExit:
\r
5292 saveSettingsOnExit = !saveSettingsOnExit;
\r
5293 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5294 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5295 MF_CHECKED : MF_UNCHECKED));
\r
5306 case IDM_AboutGame:
\r
5311 appData.debugMode = !appData.debugMode;
\r
5312 if (appData.debugMode) {
\r
5313 char dir[MSG_SIZ];
\r
5314 GetCurrentDirectory(MSG_SIZ, dir);
\r
5315 SetCurrentDirectory(installDir);
\r
5316 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5317 SetCurrentDirectory(dir);
\r
5318 setbuf(debugFP, NULL);
\r
5325 case IDM_HELPCONTENTS:
\r
5326 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5327 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5328 MessageBox (GetFocus(),
\r
5329 _("Unable to activate help"),
\r
5330 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5334 case IDM_HELPSEARCH:
\r
5335 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5336 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5337 MessageBox (GetFocus(),
\r
5338 _("Unable to activate help"),
\r
5339 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5343 case IDM_HELPHELP:
\r
5344 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5345 MessageBox (GetFocus(),
\r
5346 _("Unable to activate help"),
\r
5347 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5352 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5354 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5355 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5356 FreeProcInstance(lpProc);
\r
5359 case IDM_DirectCommand1:
\r
5360 AskQuestionEvent(_("Direct Command"),
\r
5361 _("Send to chess program:"), "", "1");
\r
5363 case IDM_DirectCommand2:
\r
5364 AskQuestionEvent(_("Direct Command"),
\r
5365 _("Send to second chess program:"), "", "2");
\r
5368 case EP_WhitePawn:
\r
5369 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5370 fromX = fromY = -1;
\r
5373 case EP_WhiteKnight:
\r
5374 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5375 fromX = fromY = -1;
\r
5378 case EP_WhiteBishop:
\r
5379 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5380 fromX = fromY = -1;
\r
5383 case EP_WhiteRook:
\r
5384 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5385 fromX = fromY = -1;
\r
5388 case EP_WhiteQueen:
\r
5389 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5390 fromX = fromY = -1;
\r
5393 case EP_WhiteFerz:
\r
5394 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5395 fromX = fromY = -1;
\r
5398 case EP_WhiteWazir:
\r
5399 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5400 fromX = fromY = -1;
\r
5403 case EP_WhiteAlfil:
\r
5404 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5405 fromX = fromY = -1;
\r
5408 case EP_WhiteCannon:
\r
5409 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5410 fromX = fromY = -1;
\r
5413 case EP_WhiteCardinal:
\r
5414 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5415 fromX = fromY = -1;
\r
5418 case EP_WhiteMarshall:
\r
5419 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5420 fromX = fromY = -1;
\r
5423 case EP_WhiteKing:
\r
5424 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5425 fromX = fromY = -1;
\r
5428 case EP_BlackPawn:
\r
5429 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5430 fromX = fromY = -1;
\r
5433 case EP_BlackKnight:
\r
5434 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5435 fromX = fromY = -1;
\r
5438 case EP_BlackBishop:
\r
5439 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5440 fromX = fromY = -1;
\r
5443 case EP_BlackRook:
\r
5444 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5445 fromX = fromY = -1;
\r
5448 case EP_BlackQueen:
\r
5449 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5450 fromX = fromY = -1;
\r
5453 case EP_BlackFerz:
\r
5454 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5455 fromX = fromY = -1;
\r
5458 case EP_BlackWazir:
\r
5459 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5460 fromX = fromY = -1;
\r
5463 case EP_BlackAlfil:
\r
5464 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5465 fromX = fromY = -1;
\r
5468 case EP_BlackCannon:
\r
5469 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5470 fromX = fromY = -1;
\r
5473 case EP_BlackCardinal:
\r
5474 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5475 fromX = fromY = -1;
\r
5478 case EP_BlackMarshall:
\r
5479 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5480 fromX = fromY = -1;
\r
5483 case EP_BlackKing:
\r
5484 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5485 fromX = fromY = -1;
\r
5488 case EP_EmptySquare:
\r
5489 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5490 fromX = fromY = -1;
\r
5493 case EP_ClearBoard:
\r
5494 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5495 fromX = fromY = -1;
\r
5499 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5500 fromX = fromY = -1;
\r
5504 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5505 fromX = fromY = -1;
\r
5509 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5510 fromX = fromY = -1;
\r
5514 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5515 fromX = fromY = -1;
\r
5519 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5520 fromX = fromY = -1;
\r
5524 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5525 fromX = fromY = -1;
\r
5529 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5530 fromX = fromY = -1;
\r
5534 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5535 fromX = fromY = -1;
\r
5539 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5540 fromX = fromY = -1;
\r
5544 barbaric = 0; appData.language = "";
\r
5545 TranslateMenus(0);
\r
5546 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5547 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5548 lastChecked = wmId;
\r
5552 if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)
\r
5553 RecentEngineEvent(wmId - IDM_RecentEngines);
\r
5555 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5556 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5557 TranslateMenus(0);
\r
5558 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5559 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5560 lastChecked = wmId;
\r
5563 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5569 case CLOCK_TIMER_ID:
\r
5570 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5571 clockTimerEvent = 0;
\r
5572 DecrementClocks(); /* call into back end */
\r
5574 case LOAD_GAME_TIMER_ID:
\r
5575 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5576 loadGameTimerEvent = 0;
\r
5577 AutoPlayGameLoop(); /* call into back end */
\r
5579 case ANALYSIS_TIMER_ID:
\r
5580 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5581 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5582 AnalysisPeriodicEvent(0);
\r
5584 KillTimer(hwnd, analysisTimerEvent);
\r
5585 analysisTimerEvent = 0;
\r
5588 case DELAYED_TIMER_ID:
\r
5589 KillTimer(hwnd, delayedTimerEvent);
\r
5590 delayedTimerEvent = 0;
\r
5591 delayedTimerCallback();
\r
5596 case WM_USER_Input:
\r
5597 InputEvent(hwnd, message, wParam, lParam);
\r
5600 /* [AS] Also move "attached" child windows */
\r
5601 case WM_WINDOWPOSCHANGING:
\r
5603 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5604 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5606 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5607 /* Window is moving */
\r
5610 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5611 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5612 rcMain.right = wpMain.x + wpMain.width;
\r
5613 rcMain.top = wpMain.y;
\r
5614 rcMain.bottom = wpMain.y + wpMain.height;
\r
5616 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5617 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5618 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5619 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5620 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5621 wpMain.x = lpwp->x;
\r
5622 wpMain.y = lpwp->y;
\r
5627 /* [AS] Snapping */
\r
5628 case WM_ENTERSIZEMOVE:
\r
5629 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5630 if (hwnd == hwndMain) {
\r
5631 doingSizing = TRUE;
\r
5634 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5638 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5639 if (hwnd == hwndMain) {
\r
5640 lastSizing = wParam;
\r
5645 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5646 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5648 case WM_EXITSIZEMOVE:
\r
5649 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5650 if (hwnd == hwndMain) {
\r
5652 doingSizing = FALSE;
\r
5653 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5654 GetClientRect(hwnd, &client);
\r
5655 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5657 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5659 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5662 case WM_DESTROY: /* message: window being destroyed */
\r
5663 PostQuitMessage(0);
\r
5667 if (hwnd == hwndMain) {
\r
5672 default: /* Passes it on if unprocessed */
\r
5673 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5678 /*---------------------------------------------------------------------------*\
\r
5680 * Misc utility routines
\r
5682 \*---------------------------------------------------------------------------*/
\r
5685 * Decent random number generator, at least not as bad as Windows
\r
5686 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5688 unsigned int randstate;
\r
5693 randstate = randstate * 1664525 + 1013904223;
\r
5694 return (int) randstate & 0x7fffffff;
\r
5698 mysrandom(unsigned int seed)
\r
5705 * returns TRUE if user selects a different color, FALSE otherwise
\r
5709 ChangeColor(HWND hwnd, COLORREF *which)
\r
5711 static BOOL firstTime = TRUE;
\r
5712 static DWORD customColors[16];
\r
5714 COLORREF newcolor;
\r
5719 /* Make initial colors in use available as custom colors */
\r
5720 /* Should we put the compiled-in defaults here instead? */
\r
5722 customColors[i++] = lightSquareColor & 0xffffff;
\r
5723 customColors[i++] = darkSquareColor & 0xffffff;
\r
5724 customColors[i++] = whitePieceColor & 0xffffff;
\r
5725 customColors[i++] = blackPieceColor & 0xffffff;
\r
5726 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5727 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5729 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5730 customColors[i++] = textAttribs[ccl].color;
\r
5732 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5733 firstTime = FALSE;
\r
5736 cc.lStructSize = sizeof(cc);
\r
5737 cc.hwndOwner = hwnd;
\r
5738 cc.hInstance = NULL;
\r
5739 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5740 cc.lpCustColors = (LPDWORD) customColors;
\r
5741 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5743 if (!ChooseColor(&cc)) return FALSE;
\r
5745 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5746 if (newcolor == *which) return FALSE;
\r
5747 *which = newcolor;
\r
5751 InitDrawingColors();
\r
5752 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5757 MyLoadSound(MySound *ms)
\r
5763 if (ms->data && ms->flag) free(ms->data);
\r
5766 switch (ms->name[0]) {
\r
5772 /* System sound from Control Panel. Don't preload here. */
\r
5776 if (ms->name[1] == NULLCHAR) {
\r
5777 /* "!" alone = silence */
\r
5780 /* Builtin wave resource. Error if not found. */
\r
5781 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5782 if (h == NULL) break;
\r
5783 ms->data = (void *)LoadResource(hInst, h);
\r
5784 ms->flag = 0; // not maloced, so cannot be freed!
\r
5785 if (h == NULL) break;
\r
5790 /* .wav file. Error if not found. */
\r
5791 f = fopen(ms->name, "rb");
\r
5792 if (f == NULL) break;
\r
5793 if (fstat(fileno(f), &st) < 0) break;
\r
5794 ms->data = malloc(st.st_size);
\r
5796 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5802 char buf[MSG_SIZ];
\r
5803 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5804 DisplayError(buf, GetLastError());
\r
5810 MyPlaySound(MySound *ms)
\r
5812 BOOLEAN ok = FALSE;
\r
5814 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5815 switch (ms->name[0]) {
\r
5817 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5822 /* System sound from Control Panel (deprecated feature).
\r
5823 "$" alone or an unset sound name gets default beep (still in use). */
\r
5824 if (ms->name[1]) {
\r
5825 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5827 if (!ok) ok = MessageBeep(MB_OK);
\r
5830 /* Builtin wave resource, or "!" alone for silence */
\r
5831 if (ms->name[1]) {
\r
5832 if (ms->data == NULL) return FALSE;
\r
5833 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5839 /* .wav file. Error if not found. */
\r
5840 if (ms->data == NULL) return FALSE;
\r
5841 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5844 /* Don't print an error: this can happen innocently if the sound driver
\r
5845 is busy; for instance, if another instance of WinBoard is playing
\r
5846 a sound at about the same time. */
\r
5852 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5855 OPENFILENAME *ofn;
\r
5856 static UINT *number; /* gross that this is static */
\r
5858 switch (message) {
\r
5859 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5860 /* Center the dialog over the application window */
\r
5861 ofn = (OPENFILENAME *) lParam;
\r
5862 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5863 number = (UINT *) ofn->lCustData;
\r
5864 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5868 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5869 Translate(hDlg, 1536);
\r
5870 return FALSE; /* Allow for further processing */
\r
5873 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5874 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5876 return FALSE; /* Allow for further processing */
\r
5882 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5884 static UINT *number;
\r
5885 OPENFILENAME *ofname;
\r
5888 case WM_INITDIALOG:
\r
5889 Translate(hdlg, DLG_IndexNumber);
\r
5890 ofname = (OPENFILENAME *)lParam;
\r
5891 number = (UINT *)(ofname->lCustData);
\r
5894 ofnot = (OFNOTIFY *)lParam;
\r
5895 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5896 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5905 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5906 char *nameFilt, char *dlgTitle, UINT *number,
\r
5907 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5909 OPENFILENAME openFileName;
\r
5910 char buf1[MSG_SIZ];
\r
5913 if (fileName == NULL) fileName = buf1;
\r
5914 if (defName == NULL) {
\r
5915 safeStrCpy(fileName, "*.", 3 );
\r
5916 strcat(fileName, defExt);
\r
5918 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5920 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5921 if (number) *number = 0;
\r
5923 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5924 openFileName.hwndOwner = hwnd;
\r
5925 openFileName.hInstance = (HANDLE) hInst;
\r
5926 openFileName.lpstrFilter = nameFilt;
\r
5927 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5928 openFileName.nMaxCustFilter = 0L;
\r
5929 openFileName.nFilterIndex = 1L;
\r
5930 openFileName.lpstrFile = fileName;
\r
5931 openFileName.nMaxFile = MSG_SIZ;
\r
5932 openFileName.lpstrFileTitle = fileTitle;
\r
5933 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5934 openFileName.lpstrInitialDir = NULL;
\r
5935 openFileName.lpstrTitle = dlgTitle;
\r
5936 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5937 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5938 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5939 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5940 openFileName.nFileOffset = 0;
\r
5941 openFileName.nFileExtension = 0;
\r
5942 openFileName.lpstrDefExt = defExt;
\r
5943 openFileName.lCustData = (LONG) number;
\r
5944 openFileName.lpfnHook = oldDialog ?
\r
5945 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5946 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5948 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5949 GetOpenFileName(&openFileName)) {
\r
5950 /* open the file */
\r
5951 f = fopen(openFileName.lpstrFile, write);
\r
5953 MessageBox(hwnd, _("File open failed"), NULL,
\r
5954 MB_OK|MB_ICONEXCLAMATION);
\r
5958 int err = CommDlgExtendedError();
\r
5959 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5968 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5970 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5973 * Get the first pop-up menu in the menu template. This is the
\r
5974 * menu that TrackPopupMenu displays.
\r
5976 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5977 TranslateOneMenu(10, hmenuTrackPopup);
\r
5979 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5982 * TrackPopup uses screen coordinates, so convert the
\r
5983 * coordinates of the mouse click to screen coordinates.
\r
5985 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5987 /* Draw and track the floating pop-up menu. */
\r
5988 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5989 pt.x, pt.y, 0, hwnd, NULL);
\r
5991 /* Destroy the menu.*/
\r
5992 DestroyMenu(hmenu);
\r
5997 int sizeX, sizeY, newSizeX, newSizeY;
\r
5999 } ResizeEditPlusButtonsClosure;
\r
6002 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
6004 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
6008 if (hChild == cl->hText) return TRUE;
\r
6009 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
6010 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
6011 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
6012 ScreenToClient(cl->hDlg, &pt);
\r
6013 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
6014 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
6018 /* Resize a dialog that has a (rich) edit field filling most of
\r
6019 the top, with a row of buttons below */
\r
6021 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
6024 int newTextHeight, newTextWidth;
\r
6025 ResizeEditPlusButtonsClosure cl;
\r
6027 /*if (IsIconic(hDlg)) return;*/
\r
6028 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
6030 cl.hdwp = BeginDeferWindowPos(8);
\r
6032 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
6033 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6034 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6035 if (newTextHeight < 0) {
\r
6036 newSizeY += -newTextHeight;
\r
6037 newTextHeight = 0;
\r
6039 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
6040 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6046 cl.newSizeX = newSizeX;
\r
6047 cl.newSizeY = newSizeY;
\r
6048 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
6050 EndDeferWindowPos(cl.hdwp);
\r
6053 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
6055 RECT rChild, rParent;
\r
6056 int wChild, hChild, wParent, hParent;
\r
6057 int wScreen, hScreen, xNew, yNew;
\r
6060 /* Get the Height and Width of the child window */
\r
6061 GetWindowRect (hwndChild, &rChild);
\r
6062 wChild = rChild.right - rChild.left;
\r
6063 hChild = rChild.bottom - rChild.top;
\r
6065 /* Get the Height and Width of the parent window */
\r
6066 GetWindowRect (hwndParent, &rParent);
\r
6067 wParent = rParent.right - rParent.left;
\r
6068 hParent = rParent.bottom - rParent.top;
\r
6070 /* Get the display limits */
\r
6071 hdc = GetDC (hwndChild);
\r
6072 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
6073 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
6074 ReleaseDC(hwndChild, hdc);
\r
6076 /* Calculate new X position, then adjust for screen */
\r
6077 xNew = rParent.left + ((wParent - wChild) /2);
\r
6080 } else if ((xNew+wChild) > wScreen) {
\r
6081 xNew = wScreen - wChild;
\r
6084 /* Calculate new Y position, then adjust for screen */
\r
6086 yNew = rParent.top + ((hParent - hChild) /2);
\r
6089 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
6094 } else if ((yNew+hChild) > hScreen) {
\r
6095 yNew = hScreen - hChild;
\r
6098 /* Set it, and return */
\r
6099 return SetWindowPos (hwndChild, NULL,
\r
6100 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
6103 /* Center one window over another */
\r
6104 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
6106 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
6109 /*---------------------------------------------------------------------------*\
\r
6111 * Startup Dialog functions
\r
6113 \*---------------------------------------------------------------------------*/
\r
6115 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
6117 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6119 while (*cd != NULL) {
\r
6120 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
6126 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
6128 char buf1[MAX_ARG_LEN];
\r
6131 if (str[0] == '@') {
\r
6132 FILE* f = fopen(str + 1, "r");
\r
6134 DisplayFatalError(str + 1, errno, 2);
\r
6137 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
6139 buf1[len] = NULLCHAR;
\r
6143 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6146 char buf[MSG_SIZ];
\r
6147 char *end = strchr(str, '\n');
\r
6148 if (end == NULL) return;
\r
6149 memcpy(buf, str, end - str);
\r
6150 buf[end - str] = NULLCHAR;
\r
6151 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6157 SetStartupDialogEnables(HWND hDlg)
\r
6159 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6160 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6161 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6162 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6163 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6164 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6165 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6166 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6167 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6168 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6169 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6170 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6171 IsDlgButtonChecked(hDlg, OPT_View));
\r
6175 QuoteForFilename(char *filename)
\r
6177 int dquote, space;
\r
6178 dquote = strchr(filename, '"') != NULL;
\r
6179 space = strchr(filename, ' ') != NULL;
\r
6180 if (dquote || space) {
\r
6192 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6194 char buf[MSG_SIZ];
\r
6197 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6198 q = QuoteForFilename(nthcp);
\r
6199 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6200 if (*nthdir != NULLCHAR) {
\r
6201 q = QuoteForFilename(nthdir);
\r
6202 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6204 if (*nthcp == NULLCHAR) {
\r
6205 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6206 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6207 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6208 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6213 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6215 char buf[MSG_SIZ];
\r
6219 switch (message) {
\r
6220 case WM_INITDIALOG:
\r
6221 /* Center the dialog */
\r
6222 CenterWindow (hDlg, GetDesktopWindow());
\r
6223 Translate(hDlg, DLG_Startup);
\r
6224 /* Initialize the dialog items */
\r
6225 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6226 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6227 firstChessProgramNames);
\r
6228 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6229 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6230 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6231 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6232 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6233 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6234 if (*appData.icsHelper != NULLCHAR) {
\r
6235 char *q = QuoteForFilename(appData.icsHelper);
\r
6236 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6238 if (*appData.icsHost == NULLCHAR) {
\r
6239 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6240 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6241 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6242 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6243 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6246 if (appData.icsActive) {
\r
6247 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6249 else if (appData.noChessProgram) {
\r
6250 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6253 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6256 SetStartupDialogEnables(hDlg);
\r
6260 switch (LOWORD(wParam)) {
\r
6262 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6263 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6264 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6266 comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox
\r
6267 ParseArgs(StringGet, &p);
\r
6268 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6269 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6271 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6272 ParseArgs(StringGet, &p);
\r
6273 SwapEngines(singleList); // ... and then make it 'second'
\r
6275 appData.noChessProgram = FALSE;
\r
6276 appData.icsActive = FALSE;
\r
6277 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6278 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6279 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6281 ParseArgs(StringGet, &p);
\r
6282 if (appData.zippyPlay) {
\r
6283 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6284 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6286 ParseArgs(StringGet, &p);
\r
6288 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6289 appData.noChessProgram = TRUE;
\r
6290 appData.icsActive = FALSE;
\r
6292 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6293 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6296 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6297 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6299 ParseArgs(StringGet, &p);
\r
6301 EndDialog(hDlg, TRUE);
\r
6308 case IDM_HELPCONTENTS:
\r
6309 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6310 MessageBox (GetFocus(),
\r
6311 _("Unable to activate help"),
\r
6312 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6317 SetStartupDialogEnables(hDlg);
\r
6325 /*---------------------------------------------------------------------------*\
\r
6327 * About box dialog functions
\r
6329 \*---------------------------------------------------------------------------*/
\r
6331 /* Process messages for "About" dialog box */
\r
6333 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6335 switch (message) {
\r
6336 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6337 /* Center the dialog over the application window */
\r
6338 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6339 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6340 Translate(hDlg, ABOUTBOX);
\r
6344 case WM_COMMAND: /* message: received a command */
\r
6345 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6346 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6347 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6355 /*---------------------------------------------------------------------------*\
\r
6357 * Comment Dialog functions
\r
6359 \*---------------------------------------------------------------------------*/
\r
6362 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6364 static HANDLE hwndText = NULL;
\r
6365 int len, newSizeX, newSizeY, flags;
\r
6366 static int sizeX, sizeY;
\r
6371 switch (message) {
\r
6372 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6373 /* Initialize the dialog items */
\r
6374 Translate(hDlg, DLG_EditComment);
\r
6375 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6376 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6377 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6378 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6379 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6380 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6381 SetWindowText(hDlg, commentTitle);
\r
6382 if (editComment) {
\r
6383 SetFocus(hwndText);
\r
6385 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6387 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6388 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6389 MAKELPARAM(FALSE, 0));
\r
6390 /* Size and position the dialog */
\r
6391 if (!commentDialog) {
\r
6392 commentDialog = hDlg;
\r
6393 flags = SWP_NOZORDER;
\r
6394 GetClientRect(hDlg, &rect);
\r
6395 sizeX = rect.right;
\r
6396 sizeY = rect.bottom;
\r
6397 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6398 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6399 WINDOWPLACEMENT wp;
\r
6400 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6401 wp.length = sizeof(WINDOWPLACEMENT);
\r
6403 wp.showCmd = SW_SHOW;
\r
6404 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6405 wp.rcNormalPosition.left = wpComment.x;
\r
6406 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6407 wp.rcNormalPosition.top = wpComment.y;
\r
6408 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6409 SetWindowPlacement(hDlg, &wp);
\r
6411 GetClientRect(hDlg, &rect);
\r
6412 newSizeX = rect.right;
\r
6413 newSizeY = rect.bottom;
\r
6414 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6415 newSizeX, newSizeY);
\r
6420 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6423 case WM_COMMAND: /* message: received a command */
\r
6424 switch (LOWORD(wParam)) {
\r
6426 if (editComment) {
\r
6428 /* Read changed options from the dialog box */
\r
6429 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6430 len = GetWindowTextLength(hwndText);
\r
6431 str = (char *) malloc(len + 1);
\r
6432 GetWindowText(hwndText, str, len + 1);
\r
6441 ReplaceComment(commentIndex, str);
\r
6448 case OPT_CancelComment:
\r
6452 case OPT_ClearComment:
\r
6453 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6456 case OPT_EditComment:
\r
6457 EditCommentEvent();
\r
6465 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6466 if( wParam == OPT_CommentText ) {
\r
6467 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6469 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6470 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6474 pt.x = LOWORD( lpMF->lParam );
\r
6475 pt.y = HIWORD( lpMF->lParam );
\r
6477 if(lpMF->msg == WM_CHAR) {
\r
6479 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6480 index = sel.cpMin;
\r
6482 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6484 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6485 len = GetWindowTextLength(hwndText);
\r
6486 str = (char *) malloc(len + 1);
\r
6487 GetWindowText(hwndText, str, len + 1);
\r
6488 ReplaceComment(commentIndex, str);
\r
6489 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6490 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6493 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6494 lpMF->msg = WM_USER;
\r
6502 newSizeX = LOWORD(lParam);
\r
6503 newSizeY = HIWORD(lParam);
\r
6504 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6509 case WM_GETMINMAXINFO:
\r
6510 /* Prevent resizing window too small */
\r
6511 mmi = (MINMAXINFO *) lParam;
\r
6512 mmi->ptMinTrackSize.x = 100;
\r
6513 mmi->ptMinTrackSize.y = 100;
\r
6520 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6525 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6527 if (str == NULL) str = "";
\r
6528 p = (char *) malloc(2 * strlen(str) + 2);
\r
6531 if (*str == '\n') *q++ = '\r';
\r
6535 if (commentText != NULL) free(commentText);
\r
6537 commentIndex = index;
\r
6538 commentTitle = title;
\r
6540 editComment = edit;
\r
6542 if (commentDialog) {
\r
6543 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6544 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6546 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6547 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6548 hwndMain, (DLGPROC)lpProc);
\r
6549 FreeProcInstance(lpProc);
\r
6555 /*---------------------------------------------------------------------------*\
\r
6557 * Type-in move dialog functions
\r
6559 \*---------------------------------------------------------------------------*/
\r
6562 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6564 char move[MSG_SIZ];
\r
6567 switch (message) {
\r
6568 case WM_INITDIALOG:
\r
6569 move[0] = (char) lParam;
\r
6570 move[1] = NULLCHAR;
\r
6571 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6572 Translate(hDlg, DLG_TypeInMove);
\r
6573 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6574 SetWindowText(hInput, move);
\r
6576 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6580 switch (LOWORD(wParam)) {
\r
6583 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6584 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6585 TypeInDoneEvent(move);
\r
6586 EndDialog(hDlg, TRUE);
\r
6589 EndDialog(hDlg, FALSE);
\r
6600 PopUpMoveDialog(char firstchar)
\r
6604 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6605 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6606 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6607 FreeProcInstance(lpProc);
\r
6610 /*---------------------------------------------------------------------------*\
\r
6612 * Type-in name dialog functions
\r
6614 \*---------------------------------------------------------------------------*/
\r
6617 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6619 char move[MSG_SIZ];
\r
6622 switch (message) {
\r
6623 case WM_INITDIALOG:
\r
6624 move[0] = (char) lParam;
\r
6625 move[1] = NULLCHAR;
\r
6626 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6627 Translate(hDlg, DLG_TypeInName);
\r
6628 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6629 SetWindowText(hInput, move);
\r
6631 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6635 switch (LOWORD(wParam)) {
\r
6637 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6638 appData.userName = strdup(move);
\r
6641 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6642 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6643 DisplayTitle(move);
\r
6647 EndDialog(hDlg, TRUE);
\r
6650 EndDialog(hDlg, FALSE);
\r
6661 PopUpNameDialog(char firstchar)
\r
6665 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6666 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6667 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6668 FreeProcInstance(lpProc);
\r
6671 /*---------------------------------------------------------------------------*\
\r
6675 \*---------------------------------------------------------------------------*/
\r
6677 /* Nonmodal error box */
\r
6678 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6679 WPARAM wParam, LPARAM lParam);
\r
6682 ErrorPopUp(char *title, char *content)
\r
6686 BOOLEAN modal = hwndMain == NULL;
\r
6704 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6705 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6708 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6710 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6711 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6712 hwndMain, (DLGPROC)lpProc);
\r
6713 FreeProcInstance(lpProc);
\r
6720 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6721 if (errorDialog == NULL) return;
\r
6722 DestroyWindow(errorDialog);
\r
6723 errorDialog = NULL;
\r
6724 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6728 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6733 switch (message) {
\r
6734 case WM_INITDIALOG:
\r
6735 GetWindowRect(hDlg, &rChild);
\r
6738 SetWindowPos(hDlg, NULL, rChild.left,
\r
6739 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6740 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6744 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6745 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6746 and it doesn't work when you resize the dialog.
\r
6747 For now, just give it a default position.
\r
6749 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6750 Translate(hDlg, DLG_Error);
\r
6752 errorDialog = hDlg;
\r
6753 SetWindowText(hDlg, errorTitle);
\r
6754 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6755 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6759 switch (LOWORD(wParam)) {
\r
6762 if (errorDialog == hDlg) errorDialog = NULL;
\r
6763 DestroyWindow(hDlg);
\r
6775 HWND gothicDialog = NULL;
\r
6778 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6782 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6784 switch (message) {
\r
6785 case WM_INITDIALOG:
\r
6786 GetWindowRect(hDlg, &rChild);
\r
6788 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6792 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6793 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6794 and it doesn't work when you resize the dialog.
\r
6795 For now, just give it a default position.
\r
6797 gothicDialog = hDlg;
\r
6798 SetWindowText(hDlg, errorTitle);
\r
6799 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6800 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6804 switch (LOWORD(wParam)) {
\r
6807 if (errorDialog == hDlg) errorDialog = NULL;
\r
6808 DestroyWindow(hDlg);
\r
6820 GothicPopUp(char *title, VariantClass variant)
\r
6823 static char *lastTitle;
\r
6825 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6826 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6828 if(lastTitle != title && gothicDialog != NULL) {
\r
6829 DestroyWindow(gothicDialog);
\r
6830 gothicDialog = NULL;
\r
6832 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6833 title = lastTitle;
\r
6834 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6835 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6836 hwndMain, (DLGPROC)lpProc);
\r
6837 FreeProcInstance(lpProc);
\r
6842 /*---------------------------------------------------------------------------*\
\r
6844 * Ics Interaction console functions
\r
6846 \*---------------------------------------------------------------------------*/
\r
6848 #define HISTORY_SIZE 64
\r
6849 static char *history[HISTORY_SIZE];
\r
6850 int histIn = 0, histP = 0;
\r
6854 SaveInHistory(char *cmd)
\r
6856 if (history[histIn] != NULL) {
\r
6857 free(history[histIn]);
\r
6858 history[histIn] = NULL;
\r
6860 if (*cmd == NULLCHAR) return;
\r
6861 history[histIn] = StrSave(cmd);
\r
6862 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6863 if (history[histIn] != NULL) {
\r
6864 free(history[histIn]);
\r
6866 history[histIn] = NULL;
\r
6872 PrevInHistory(char *cmd)
\r
6875 if (histP == histIn) {
\r
6876 if (history[histIn] != NULL) free(history[histIn]);
\r
6877 history[histIn] = StrSave(cmd);
\r
6879 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6880 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6882 return history[histP];
\r
6888 if (histP == histIn) return NULL;
\r
6889 histP = (histP + 1) % HISTORY_SIZE;
\r
6890 return history[histP];
\r
6894 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6898 hmenu = LoadMenu(hInst, "TextMenu");
\r
6899 h = GetSubMenu(hmenu, 0);
\r
6901 if (strcmp(e->item, "-") == 0) {
\r
6902 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6903 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6904 int flags = MF_STRING, j = 0;
\r
6905 if (e->item[0] == '|') {
\r
6906 flags |= MF_MENUBARBREAK;
\r
6909 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6910 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6918 WNDPROC consoleTextWindowProc;
\r
6921 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6923 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6924 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6928 SetWindowText(hInput, command);
\r
6930 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6932 sel.cpMin = 999999;
\r
6933 sel.cpMax = 999999;
\r
6934 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6939 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6940 if (sel.cpMin == sel.cpMax) {
\r
6941 /* Expand to surrounding word */
\r
6944 tr.chrg.cpMax = sel.cpMin;
\r
6945 tr.chrg.cpMin = --sel.cpMin;
\r
6946 if (sel.cpMin < 0) break;
\r
6947 tr.lpstrText = name;
\r
6948 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6949 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6953 tr.chrg.cpMin = sel.cpMax;
\r
6954 tr.chrg.cpMax = ++sel.cpMax;
\r
6955 tr.lpstrText = name;
\r
6956 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6957 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6960 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6961 MessageBeep(MB_ICONEXCLAMATION);
\r
6965 tr.lpstrText = name;
\r
6966 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6968 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6969 MessageBeep(MB_ICONEXCLAMATION);
\r
6972 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6975 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
6976 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6977 SetWindowText(hInput, buf);
\r
6978 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6980 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6981 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6982 SetWindowText(hInput, buf);
\r
6983 sel.cpMin = 999999;
\r
6984 sel.cpMax = 999999;
\r
6985 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6991 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6996 switch (message) {
\r
6998 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6999 if(wParam=='R') return 0;
\r
7002 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
7005 sel.cpMin = 999999;
\r
7006 sel.cpMax = 999999;
\r
7007 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7008 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
7013 if(wParam != '\022') {
\r
7014 if (wParam == '\t') {
\r
7015 if (GetKeyState(VK_SHIFT) < 0) {
\r
7017 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7018 if (buttonDesc[0].hwnd) {
\r
7019 SetFocus(buttonDesc[0].hwnd);
\r
7021 SetFocus(hwndMain);
\r
7025 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
7028 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7029 JAWS_DELETE( SetFocus(hInput); )
\r
7030 SendMessage(hInput, message, wParam, lParam);
\r
7033 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
7035 case WM_RBUTTONDOWN:
\r
7036 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
7037 /* Move selection here if it was empty */
\r
7039 pt.x = LOWORD(lParam);
\r
7040 pt.y = HIWORD(lParam);
\r
7041 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7042 if (sel.cpMin == sel.cpMax) {
\r
7043 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
7044 sel.cpMax = sel.cpMin;
\r
7045 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7047 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
7048 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
7050 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
7051 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7052 if (sel.cpMin == sel.cpMax) {
\r
7053 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7054 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
7056 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7057 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7059 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
7060 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
7061 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
7062 MenuPopup(hwnd, pt, hmenu, -1);
\r
7066 case WM_RBUTTONUP:
\r
7067 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7068 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7069 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7073 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7075 return SendMessage(hInput, message, wParam, lParam);
\r
7076 case WM_MBUTTONDOWN:
\r
7077 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7079 switch (LOWORD(wParam)) {
\r
7080 case IDM_QuickPaste:
\r
7082 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7083 if (sel.cpMin == sel.cpMax) {
\r
7084 MessageBeep(MB_ICONEXCLAMATION);
\r
7087 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7088 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7089 SendMessage(hInput, WM_PASTE, 0, 0);
\r
7094 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7097 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7100 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7104 int i = LOWORD(wParam) - IDM_CommandX;
\r
7105 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
7106 icsTextMenuEntry[i].command != NULL) {
\r
7107 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
7108 icsTextMenuEntry[i].getname,
\r
7109 icsTextMenuEntry[i].immediate);
\r
7117 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
7120 WNDPROC consoleInputWindowProc;
\r
7123 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7125 char buf[MSG_SIZ];
\r
7127 static BOOL sendNextChar = FALSE;
\r
7128 static BOOL quoteNextChar = FALSE;
\r
7129 InputSource *is = consoleInputSource;
\r
7133 switch (message) {
\r
7135 if (!appData.localLineEditing || sendNextChar) {
\r
7136 is->buf[0] = (CHAR) wParam;
\r
7138 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7139 sendNextChar = FALSE;
\r
7142 if (quoteNextChar) {
\r
7143 buf[0] = (char) wParam;
\r
7144 buf[1] = NULLCHAR;
\r
7145 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7146 quoteNextChar = FALSE;
\r
7150 case '\r': /* Enter key */
\r
7151 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7152 if (consoleEcho) SaveInHistory(is->buf);
\r
7153 is->buf[is->count++] = '\n';
\r
7154 is->buf[is->count] = NULLCHAR;
\r
7155 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7156 if (consoleEcho) {
\r
7157 ConsoleOutput(is->buf, is->count, TRUE);
\r
7158 } else if (appData.localLineEditing) {
\r
7159 ConsoleOutput("\n", 1, TRUE);
\r
7162 case '\033': /* Escape key */
\r
7163 SetWindowText(hwnd, "");
\r
7164 cf.cbSize = sizeof(CHARFORMAT);
\r
7165 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7166 if (consoleEcho) {
\r
7167 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7169 cf.crTextColor = COLOR_ECHOOFF;
\r
7171 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7172 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7174 case '\t': /* Tab key */
\r
7175 if (GetKeyState(VK_SHIFT) < 0) {
\r
7177 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7180 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7181 if (buttonDesc[0].hwnd) {
\r
7182 SetFocus(buttonDesc[0].hwnd);
\r
7184 SetFocus(hwndMain);
\r
7188 case '\023': /* Ctrl+S */
\r
7189 sendNextChar = TRUE;
\r
7191 case '\021': /* Ctrl+Q */
\r
7192 quoteNextChar = TRUE;
\r
7202 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7203 p = PrevInHistory(buf);
\r
7205 SetWindowText(hwnd, p);
\r
7206 sel.cpMin = 999999;
\r
7207 sel.cpMax = 999999;
\r
7208 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7213 p = NextInHistory();
\r
7215 SetWindowText(hwnd, p);
\r
7216 sel.cpMin = 999999;
\r
7217 sel.cpMax = 999999;
\r
7218 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7224 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7228 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7232 case WM_MBUTTONDOWN:
\r
7233 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7234 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7236 case WM_RBUTTONUP:
\r
7237 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7238 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7239 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7243 hmenu = LoadMenu(hInst, "InputMenu");
\r
7244 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7245 if (sel.cpMin == sel.cpMax) {
\r
7246 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7247 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7249 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7250 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7252 pt.x = LOWORD(lParam);
\r
7253 pt.y = HIWORD(lParam);
\r
7254 MenuPopup(hwnd, pt, hmenu, -1);
\r
7258 switch (LOWORD(wParam)) {
\r
7260 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7262 case IDM_SelectAll:
\r
7264 sel.cpMax = -1; /*999999?*/
\r
7265 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7268 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7271 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7274 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7279 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7282 #define CO_MAX 100000
\r
7283 #define CO_TRIM 1000
\r
7286 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7288 static SnapData sd;
\r
7289 HWND hText, hInput;
\r
7291 static int sizeX, sizeY;
\r
7292 int newSizeX, newSizeY;
\r
7296 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7297 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7299 switch (message) {
\r
7301 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7303 ENLINK *pLink = (ENLINK*)lParam;
\r
7304 if (pLink->msg == WM_LBUTTONUP)
\r
7308 tr.chrg = pLink->chrg;
\r
7309 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7310 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7311 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7312 free(tr.lpstrText);
\r
7316 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7317 hwndConsole = hDlg;
\r
7319 consoleTextWindowProc = (WNDPROC)
\r
7320 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7321 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7322 consoleInputWindowProc = (WNDPROC)
\r
7323 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7324 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7325 Colorize(ColorNormal, TRUE);
\r
7326 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7327 ChangedConsoleFont();
\r
7328 GetClientRect(hDlg, &rect);
\r
7329 sizeX = rect.right;
\r
7330 sizeY = rect.bottom;
\r
7331 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7332 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7333 WINDOWPLACEMENT wp;
\r
7334 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7335 wp.length = sizeof(WINDOWPLACEMENT);
\r
7337 wp.showCmd = SW_SHOW;
\r
7338 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7339 wp.rcNormalPosition.left = wpConsole.x;
\r
7340 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7341 wp.rcNormalPosition.top = wpConsole.y;
\r
7342 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7343 SetWindowPlacement(hDlg, &wp);
\r
7346 // [HGM] Chessknight's change 2004-07-13
\r
7347 else { /* Determine Defaults */
\r
7348 WINDOWPLACEMENT wp;
\r
7349 wpConsole.x = wpMain.width + 1;
\r
7350 wpConsole.y = wpMain.y;
\r
7351 wpConsole.width = screenWidth - wpMain.width;
\r
7352 wpConsole.height = wpMain.height;
\r
7353 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7354 wp.length = sizeof(WINDOWPLACEMENT);
\r
7356 wp.showCmd = SW_SHOW;
\r
7357 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7358 wp.rcNormalPosition.left = wpConsole.x;
\r
7359 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7360 wp.rcNormalPosition.top = wpConsole.y;
\r
7361 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7362 SetWindowPlacement(hDlg, &wp);
\r
7365 // Allow hText to highlight URLs and send notifications on them
\r
7366 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7367 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7368 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7369 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7383 if (IsIconic(hDlg)) break;
\r
7384 newSizeX = LOWORD(lParam);
\r
7385 newSizeY = HIWORD(lParam);
\r
7386 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7387 RECT rectText, rectInput;
\r
7389 int newTextHeight, newTextWidth;
\r
7390 GetWindowRect(hText, &rectText);
\r
7391 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7392 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7393 if (newTextHeight < 0) {
\r
7394 newSizeY += -newTextHeight;
\r
7395 newTextHeight = 0;
\r
7397 SetWindowPos(hText, NULL, 0, 0,
\r
7398 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7399 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7400 pt.x = rectInput.left;
\r
7401 pt.y = rectInput.top + newSizeY - sizeY;
\r
7402 ScreenToClient(hDlg, &pt);
\r
7403 SetWindowPos(hInput, NULL,
\r
7404 pt.x, pt.y, /* needs client coords */
\r
7405 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7406 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7412 case WM_GETMINMAXINFO:
\r
7413 /* Prevent resizing window too small */
\r
7414 mmi = (MINMAXINFO *) lParam;
\r
7415 mmi->ptMinTrackSize.x = 100;
\r
7416 mmi->ptMinTrackSize.y = 100;
\r
7419 /* [AS] Snapping */
\r
7420 case WM_ENTERSIZEMOVE:
\r
7421 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7424 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7427 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7429 case WM_EXITSIZEMOVE:
\r
7430 UpdateICSWidth(hText);
\r
7431 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7434 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7442 if (hwndConsole) return;
\r
7443 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7444 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7449 ConsoleOutput(char* data, int length, int forceVisible)
\r
7454 char buf[CO_MAX+1];
\r
7457 static int delayLF = 0;
\r
7458 CHARRANGE savesel, sel;
\r
7460 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7468 while (length--) {
\r
7476 } else if (*p == '\007') {
\r
7477 MyPlaySound(&sounds[(int)SoundBell]);
\r
7484 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7485 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7486 /* Save current selection */
\r
7487 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7488 exlen = GetWindowTextLength(hText);
\r
7489 /* Find out whether current end of text is visible */
\r
7490 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7491 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7492 /* Trim existing text if it's too long */
\r
7493 if (exlen + (q - buf) > CO_MAX) {
\r
7494 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7497 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7498 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7500 savesel.cpMin -= trim;
\r
7501 savesel.cpMax -= trim;
\r
7502 if (exlen < 0) exlen = 0;
\r
7503 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7504 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7506 /* Append the new text */
\r
7507 sel.cpMin = exlen;
\r
7508 sel.cpMax = exlen;
\r
7509 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7510 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7511 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7512 if (forceVisible || exlen == 0 ||
\r
7513 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7514 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7515 /* Scroll to make new end of text visible if old end of text
\r
7516 was visible or new text is an echo of user typein */
\r
7517 sel.cpMin = 9999999;
\r
7518 sel.cpMax = 9999999;
\r
7519 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7520 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7521 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7522 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7524 if (savesel.cpMax == exlen || forceVisible) {
\r
7525 /* Move insert point to new end of text if it was at the old
\r
7526 end of text or if the new text is an echo of user typein */
\r
7527 sel.cpMin = 9999999;
\r
7528 sel.cpMax = 9999999;
\r
7529 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7531 /* Restore previous selection */
\r
7532 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7534 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7541 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7545 COLORREF oldFg, oldBg;
\r
7549 if(copyNumber > 1)
\r
7550 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7552 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7553 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7554 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7557 rect.right = x + squareSize;
\r
7559 rect.bottom = y + squareSize;
\r
7562 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7563 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7564 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7565 &rect, str, strlen(str), NULL);
\r
7567 (void) SetTextColor(hdc, oldFg);
\r
7568 (void) SetBkColor(hdc, oldBg);
\r
7569 (void) SelectObject(hdc, oldFont);
\r
7573 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7574 RECT *rect, char *color, char *flagFell)
\r
7578 COLORREF oldFg, oldBg;
\r
7581 if (twoBoards && partnerUp) return;
\r
7582 if (appData.clockMode) {
\r
7584 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7586 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7593 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7594 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7596 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7597 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7599 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7603 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7604 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7605 rect, str, strlen(str), NULL);
\r
7606 if(logoHeight > 0 && appData.clockMode) {
\r
7608 str += strlen(color)+2;
\r
7609 r.top = rect->top + logoHeight/2;
\r
7610 r.left = rect->left;
\r
7611 r.right = rect->right;
\r
7612 r.bottom = rect->bottom;
\r
7613 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7614 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7615 &r, str, strlen(str), NULL);
\r
7617 (void) SetTextColor(hdc, oldFg);
\r
7618 (void) SetBkColor(hdc, oldBg);
\r
7619 (void) SelectObject(hdc, oldFont);
\r
7624 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7630 if( count <= 0 ) {
\r
7631 if (appData.debugMode) {
\r
7632 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7635 return ERROR_INVALID_USER_BUFFER;
\r
7638 ResetEvent(ovl->hEvent);
\r
7639 ovl->Offset = ovl->OffsetHigh = 0;
\r
7640 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7644 err = GetLastError();
\r
7645 if (err == ERROR_IO_PENDING) {
\r
7646 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7650 err = GetLastError();
\r
7657 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7662 ResetEvent(ovl->hEvent);
\r
7663 ovl->Offset = ovl->OffsetHigh = 0;
\r
7664 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7668 err = GetLastError();
\r
7669 if (err == ERROR_IO_PENDING) {
\r
7670 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7674 err = GetLastError();
\r
7680 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7681 void CheckForInputBufferFull( InputSource * is )
\r
7683 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7684 /* Look for end of line */
\r
7685 char * p = is->buf;
\r
7687 while( p < is->next && *p != '\n' ) {
\r
7691 if( p >= is->next ) {
\r
7692 if (appData.debugMode) {
\r
7693 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7696 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7697 is->count = (DWORD) -1;
\r
7698 is->next = is->buf;
\r
7704 InputThread(LPVOID arg)
\r
7709 is = (InputSource *) arg;
\r
7710 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7711 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7712 while (is->hThread != NULL) {
\r
7713 is->error = DoReadFile(is->hFile, is->next,
\r
7714 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7715 &is->count, &ovl);
\r
7716 if (is->error == NO_ERROR) {
\r
7717 is->next += is->count;
\r
7719 if (is->error == ERROR_BROKEN_PIPE) {
\r
7720 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7723 is->count = (DWORD) -1;
\r
7724 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7729 CheckForInputBufferFull( is );
\r
7731 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7733 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7735 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7738 CloseHandle(ovl.hEvent);
\r
7739 CloseHandle(is->hFile);
\r
7741 if (appData.debugMode) {
\r
7742 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7749 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7751 NonOvlInputThread(LPVOID arg)
\r
7758 is = (InputSource *) arg;
\r
7759 while (is->hThread != NULL) {
\r
7760 is->error = ReadFile(is->hFile, is->next,
\r
7761 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7762 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7763 if (is->error == NO_ERROR) {
\r
7764 /* Change CRLF to LF */
\r
7765 if (is->next > is->buf) {
\r
7767 i = is->count + 1;
\r
7775 if (prev == '\r' && *p == '\n') {
\r
7787 if (is->error == ERROR_BROKEN_PIPE) {
\r
7788 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7791 is->count = (DWORD) -1;
\r
7795 CheckForInputBufferFull( is );
\r
7797 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7799 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7801 if (is->count < 0) break; /* Quit on error */
\r
7803 CloseHandle(is->hFile);
\r
7808 SocketInputThread(LPVOID arg)
\r
7812 is = (InputSource *) arg;
\r
7813 while (is->hThread != NULL) {
\r
7814 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7815 if ((int)is->count == SOCKET_ERROR) {
\r
7816 is->count = (DWORD) -1;
\r
7817 is->error = WSAGetLastError();
\r
7819 is->error = NO_ERROR;
\r
7820 is->next += is->count;
\r
7821 if (is->count == 0 && is->second == is) {
\r
7822 /* End of file on stderr; quit with no message */
\r
7826 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7828 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7830 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7836 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7840 is = (InputSource *) lParam;
\r
7841 if (is->lineByLine) {
\r
7842 /* Feed in lines one by one */
\r
7843 char *p = is->buf;
\r
7845 while (q < is->next) {
\r
7846 if (*q++ == '\n') {
\r
7847 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7852 /* Move any partial line to the start of the buffer */
\r
7854 while (p < is->next) {
\r
7859 if (is->error != NO_ERROR || is->count == 0) {
\r
7860 /* Notify backend of the error. Note: If there was a partial
\r
7861 line at the end, it is not flushed through. */
\r
7862 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7865 /* Feed in the whole chunk of input at once */
\r
7866 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7867 is->next = is->buf;
\r
7871 /*---------------------------------------------------------------------------*\
\r
7873 * Menu enables. Used when setting various modes.
\r
7875 \*---------------------------------------------------------------------------*/
\r
7883 GreyRevert(Boolean grey)
\r
7884 { // [HGM] vari: for retracting variations in local mode
\r
7885 HMENU hmenu = GetMenu(hwndMain);
\r
7886 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7887 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7891 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7893 while (enab->item > 0) {
\r
7894 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7899 Enables gnuEnables[] = {
\r
7900 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7901 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7902 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7903 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7904 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7905 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7906 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7907 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7908 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7909 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7910 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7911 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7912 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7914 // Needed to switch from ncp to GNU mode on Engine Load
\r
7915 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7916 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7917 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7918 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7919 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7920 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7921 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
7922 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7923 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
7924 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
7925 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7926 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7927 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7928 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7932 Enables icsEnables[] = {
\r
7933 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7934 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7935 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7936 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7937 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7938 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7939 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7940 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7941 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7942 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7943 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7944 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7945 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7946 { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },
\r
7947 { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },
\r
7948 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7949 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7950 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7951 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7952 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
7957 Enables zippyEnables[] = {
\r
7958 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7959 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7960 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7961 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7966 Enables ncpEnables[] = {
\r
7967 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7968 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7969 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7970 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7971 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7972 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7973 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7974 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7975 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7976 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7977 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7978 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7979 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7980 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7981 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7982 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7983 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7984 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7985 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7986 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7987 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7988 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
7992 Enables trainingOnEnables[] = {
\r
7993 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7994 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
7995 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7996 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7997 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7998 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7999 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
8000 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8001 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
8005 Enables trainingOffEnables[] = {
\r
8006 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
8007 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
8008 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
8009 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
8010 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
8011 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
8012 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
8013 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8014 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
8018 /* These modify either ncpEnables or gnuEnables */
\r
8019 Enables cmailEnables[] = {
\r
8020 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
8021 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
8022 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
8023 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
8024 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
8025 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
8026 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
8030 Enables machineThinkingEnables[] = {
\r
8031 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8032 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
8033 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
8034 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8035 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
8036 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8037 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8038 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8039 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8040 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
8041 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8042 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8043 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8044 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8045 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
8046 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
8050 Enables userThinkingEnables[] = {
\r
8051 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8052 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
8053 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
8054 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8055 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
8056 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8057 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8058 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8059 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8060 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
8061 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
8062 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
8063 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
8064 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
8065 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
8066 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
8070 /*---------------------------------------------------------------------------*\
\r
8072 * Front-end interface functions exported by XBoard.
\r
8073 * Functions appear in same order as prototypes in frontend.h.
\r
8075 \*---------------------------------------------------------------------------*/
\r
8077 CheckMark(UINT item, int state)
\r
8079 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
8085 static UINT prevChecked = 0;
\r
8086 static int prevPausing = 0;
\r
8089 if (pausing != prevPausing) {
\r
8090 prevPausing = pausing;
\r
8091 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
8092 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
8093 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
8096 switch (gameMode) {
\r
8097 case BeginningOfGame:
\r
8098 if (appData.icsActive)
\r
8099 nowChecked = IDM_IcsClient;
\r
8100 else if (appData.noChessProgram)
\r
8101 nowChecked = IDM_EditGame;
\r
8103 nowChecked = IDM_MachineBlack;
\r
8105 case MachinePlaysBlack:
\r
8106 nowChecked = IDM_MachineBlack;
\r
8108 case MachinePlaysWhite:
\r
8109 nowChecked = IDM_MachineWhite;
\r
8111 case TwoMachinesPlay:
\r
8112 nowChecked = IDM_TwoMachines;
\r
8115 nowChecked = IDM_AnalysisMode;
\r
8118 nowChecked = IDM_AnalyzeFile;
\r
8121 nowChecked = IDM_EditGame;
\r
8123 case PlayFromGameFile:
\r
8124 nowChecked = IDM_LoadGame;
\r
8126 case EditPosition:
\r
8127 nowChecked = IDM_EditPosition;
\r
8130 nowChecked = IDM_Training;
\r
8132 case IcsPlayingWhite:
\r
8133 case IcsPlayingBlack:
\r
8134 case IcsObserving:
\r
8136 nowChecked = IDM_IcsClient;
\r
8143 CheckMark(prevChecked, MF_UNCHECKED);
\r
8144 CheckMark(nowChecked, MF_CHECKED);
\r
8145 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8147 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8148 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8149 MF_BYCOMMAND|MF_ENABLED);
\r
8151 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8152 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8155 prevChecked = nowChecked;
\r
8157 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8158 if (appData.icsActive) {
\r
8159 if (appData.icsEngineAnalyze) {
\r
8160 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8162 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8165 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8171 HMENU hmenu = GetMenu(hwndMain);
\r
8172 SetMenuEnables(hmenu, icsEnables);
\r
8173 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8174 MF_BYCOMMAND|MF_ENABLED);
\r
8176 if (appData.zippyPlay) {
\r
8177 SetMenuEnables(hmenu, zippyEnables);
\r
8178 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8179 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8180 MF_BYCOMMAND|MF_ENABLED);
\r
8188 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8194 HMENU hmenu = GetMenu(hwndMain);
\r
8195 SetMenuEnables(hmenu, ncpEnables);
\r
8196 DrawMenuBar(hwndMain);
\r
8202 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8206 SetTrainingModeOn()
\r
8209 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8210 for (i = 0; i < N_BUTTONS; i++) {
\r
8211 if (buttonDesc[i].hwnd != NULL)
\r
8212 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8217 VOID SetTrainingModeOff()
\r
8220 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8221 for (i = 0; i < N_BUTTONS; i++) {
\r
8222 if (buttonDesc[i].hwnd != NULL)
\r
8223 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8229 SetUserThinkingEnables()
\r
8231 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8235 SetMachineThinkingEnables()
\r
8237 HMENU hMenu = GetMenu(hwndMain);
\r
8238 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8240 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8242 if (gameMode == MachinePlaysBlack) {
\r
8243 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8244 } else if (gameMode == MachinePlaysWhite) {
\r
8245 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8246 } else if (gameMode == TwoMachinesPlay) {
\r
8247 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8253 DisplayTitle(char *str)
\r
8255 char title[MSG_SIZ], *host;
\r
8256 if (str[0] != NULLCHAR) {
\r
8257 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8258 } else if (appData.icsActive) {
\r
8259 if (appData.icsCommPort[0] != NULLCHAR)
\r
8262 host = appData.icsHost;
\r
8263 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8264 } else if (appData.noChessProgram) {
\r
8265 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8267 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8268 strcat(title, ": ");
\r
8269 strcat(title, first.tidy);
\r
8271 SetWindowText(hwndMain, title);
\r
8276 DisplayMessage(char *str1, char *str2)
\r
8280 int remain = MESSAGE_TEXT_MAX - 1;
\r
8283 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8284 messageText[0] = NULLCHAR;
\r
8286 len = strlen(str1);
\r
8287 if (len > remain) len = remain;
\r
8288 strncpy(messageText, str1, len);
\r
8289 messageText[len] = NULLCHAR;
\r
8292 if (*str2 && remain >= 2) {
\r
8294 strcat(messageText, " ");
\r
8297 len = strlen(str2);
\r
8298 if (len > remain) len = remain;
\r
8299 strncat(messageText, str2, len);
\r
8301 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8302 safeStrCpy(lastMsg, messageText, MSG_SIZ);
\r
8304 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8308 hdc = GetDC(hwndMain);
\r
8309 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8310 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8311 &messageRect, messageText, strlen(messageText), NULL);
\r
8312 (void) SelectObject(hdc, oldFont);
\r
8313 (void) ReleaseDC(hwndMain, hdc);
\r
8317 DisplayError(char *str, int error)
\r
8319 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8323 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8325 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8326 NULL, error, LANG_NEUTRAL,
\r
8327 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8329 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8331 ErrorMap *em = errmap;
\r
8332 while (em->err != 0 && em->err != error) em++;
\r
8333 if (em->err != 0) {
\r
8334 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8336 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8341 ErrorPopUp(_("Error"), buf);
\r
8346 DisplayMoveError(char *str)
\r
8348 fromX = fromY = -1;
\r
8349 ClearHighlights();
\r
8350 DrawPosition(FALSE, NULL);
\r
8351 if (appData.popupMoveErrors) {
\r
8352 ErrorPopUp(_("Error"), str);
\r
8354 DisplayMessage(str, "");
\r
8355 moveErrorMessageUp = TRUE;
\r
8360 DisplayFatalError(char *str, int error, int exitStatus)
\r
8362 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8364 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8367 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8368 NULL, error, LANG_NEUTRAL,
\r
8369 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8371 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8373 ErrorMap *em = errmap;
\r
8374 while (em->err != 0 && em->err != error) em++;
\r
8375 if (em->err != 0) {
\r
8376 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8378 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8383 if (appData.debugMode) {
\r
8384 fprintf(debugFP, "%s: %s\n", label, str);
\r
8386 if (appData.popupExitMessage) {
\r
8387 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8388 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8390 ExitEvent(exitStatus);
\r
8395 DisplayInformation(char *str)
\r
8397 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8402 DisplayNote(char *str)
\r
8404 ErrorPopUp(_("Note"), str);
\r
8409 char *title, *question, *replyPrefix;
\r
8414 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8416 static QuestionParams *qp;
\r
8417 char reply[MSG_SIZ];
\r
8420 switch (message) {
\r
8421 case WM_INITDIALOG:
\r
8422 qp = (QuestionParams *) lParam;
\r
8423 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8424 Translate(hDlg, DLG_Question);
\r
8425 SetWindowText(hDlg, qp->title);
\r
8426 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8427 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8431 switch (LOWORD(wParam)) {
\r
8433 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8434 if (*reply) strcat(reply, " ");
\r
8435 len = strlen(reply);
\r
8436 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8437 strcat(reply, "\n");
\r
8438 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8439 EndDialog(hDlg, TRUE);
\r
8440 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8443 EndDialog(hDlg, FALSE);
\r
8454 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8456 QuestionParams qp;
\r
8460 qp.question = question;
\r
8461 qp.replyPrefix = replyPrefix;
\r
8463 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8464 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8465 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8466 FreeProcInstance(lpProc);
\r
8469 /* [AS] Pick FRC position */
\r
8470 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8472 static int * lpIndexFRC;
\r
8478 case WM_INITDIALOG:
\r
8479 lpIndexFRC = (int *) lParam;
\r
8481 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8482 Translate(hDlg, DLG_NewGameFRC);
\r
8484 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8485 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8486 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8487 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8492 switch( LOWORD(wParam) ) {
\r
8494 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8495 EndDialog( hDlg, 0 );
\r
8496 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8499 EndDialog( hDlg, 1 );
\r
8501 case IDC_NFG_Edit:
\r
8502 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8503 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8505 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8508 case IDC_NFG_Random:
\r
8509 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8510 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8523 int index = appData.defaultFrcPosition;
\r
8524 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8526 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8528 if( result == 0 ) {
\r
8529 appData.defaultFrcPosition = index;
\r
8535 /* [AS] Game list options. Refactored by HGM */
\r
8537 HWND gameListOptionsDialog;
\r
8539 // low-level front-end: clear text edit / list widget
\r
8544 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8547 // low-level front-end: clear text edit / list widget
\r
8549 GLT_DeSelectList()
\r
8551 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8554 // low-level front-end: append line to text edit / list widget
\r
8556 GLT_AddToList( char *name )
\r
8559 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8563 // low-level front-end: get line from text edit / list widget
\r
8565 GLT_GetFromList( int index, char *name )
\r
8568 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8574 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8576 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8577 int idx2 = idx1 + delta;
\r
8578 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8580 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8583 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8584 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8585 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8586 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8590 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8594 case WM_INITDIALOG:
\r
8595 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8597 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8598 Translate(hDlg, DLG_GameListOptions);
\r
8600 /* Initialize list */
\r
8601 GLT_TagsToList( lpUserGLT );
\r
8603 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8608 switch( LOWORD(wParam) ) {
\r
8611 EndDialog( hDlg, 0 );
\r
8614 EndDialog( hDlg, 1 );
\r
8617 case IDC_GLT_Default:
\r
8618 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8621 case IDC_GLT_Restore:
\r
8622 GLT_TagsToList( appData.gameListTags );
\r
8626 GLT_MoveSelection( hDlg, -1 );
\r
8629 case IDC_GLT_Down:
\r
8630 GLT_MoveSelection( hDlg, +1 );
\r
8640 int GameListOptions()
\r
8643 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8645 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8647 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8649 if( result == 0 ) {
\r
8650 /* [AS] Memory leak here! */
\r
8651 appData.gameListTags = strdup( lpUserGLT );
\r
8658 DisplayIcsInteractionTitle(char *str)
\r
8660 char consoleTitle[MSG_SIZ];
\r
8662 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8663 SetWindowText(hwndConsole, consoleTitle);
\r
8665 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
8666 char buf[MSG_SIZ], *p = buf, *q;
\r
8667 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
8669 q = strchr(p, ';');
\r
8671 if(*p) ChatPopUp(p);
\r
8675 SetActiveWindow(hwndMain);
\r
8679 DrawPosition(int fullRedraw, Board board)
\r
8681 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8684 void NotifyFrontendLogin()
\r
8687 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8693 fromX = fromY = -1;
\r
8694 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8695 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8696 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8697 dragInfo.lastpos = dragInfo.pos;
\r
8698 dragInfo.start.x = dragInfo.start.y = -1;
\r
8699 dragInfo.from = dragInfo.start;
\r
8701 DrawPosition(TRUE, NULL);
\r
8708 CommentPopUp(char *title, char *str)
\r
8710 HWND hwnd = GetActiveWindow();
\r
8711 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8713 SetActiveWindow(hwnd);
\r
8717 CommentPopDown(void)
\r
8719 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8720 if (commentDialog) {
\r
8721 ShowWindow(commentDialog, SW_HIDE);
\r
8723 commentUp = FALSE;
\r
8727 EditCommentPopUp(int index, char *title, char *str)
\r
8729 EitherCommentPopUp(index, title, str, TRUE);
\r
8736 MyPlaySound(&sounds[(int)SoundRoar]);
\r
8743 MyPlaySound(&sounds[(int)SoundMove]);
\r
8746 VOID PlayIcsWinSound()
\r
8748 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8751 VOID PlayIcsLossSound()
\r
8753 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8756 VOID PlayIcsDrawSound()
\r
8758 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8761 VOID PlayIcsUnfinishedSound()
\r
8763 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8769 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8775 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8783 consoleEcho = TRUE;
\r
8784 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8785 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8786 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8795 consoleEcho = FALSE;
\r
8796 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8797 /* This works OK: set text and background both to the same color */
\r
8799 cf.crTextColor = COLOR_ECHOOFF;
\r
8800 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8801 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8804 /* No Raw()...? */
\r
8806 void Colorize(ColorClass cc, int continuation)
\r
8808 currentColorClass = cc;
\r
8809 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8810 consoleCF.crTextColor = textAttribs[cc].color;
\r
8811 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8812 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8818 static char buf[MSG_SIZ];
\r
8819 DWORD bufsiz = MSG_SIZ;
\r
8821 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8822 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8824 if (!GetUserName(buf, &bufsiz)) {
\r
8825 /*DisplayError("Error getting user name", GetLastError());*/
\r
8826 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8834 static char buf[MSG_SIZ];
\r
8835 DWORD bufsiz = MSG_SIZ;
\r
8837 if (!GetComputerName(buf, &bufsiz)) {
\r
8838 /*DisplayError("Error getting host name", GetLastError());*/
\r
8839 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8846 ClockTimerRunning()
\r
8848 return clockTimerEvent != 0;
\r
8854 if (clockTimerEvent == 0) return FALSE;
\r
8855 KillTimer(hwndMain, clockTimerEvent);
\r
8856 clockTimerEvent = 0;
\r
8861 StartClockTimer(long millisec)
\r
8863 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8864 (UINT) millisec, NULL);
\r
8868 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8871 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8873 if(appData.noGUI) return;
\r
8874 hdc = GetDC(hwndMain);
\r
8875 if (!IsIconic(hwndMain)) {
\r
8876 DisplayAClock(hdc, timeRemaining, highlight,
\r
8877 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8879 if (highlight && iconCurrent == iconBlack) {
\r
8880 iconCurrent = iconWhite;
\r
8881 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8882 if (IsIconic(hwndMain)) {
\r
8883 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8886 (void) ReleaseDC(hwndMain, hdc);
\r
8888 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8892 DisplayBlackClock(long timeRemaining, int highlight)
\r
8895 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8898 if(appData.noGUI) return;
\r
8899 hdc = GetDC(hwndMain);
\r
8900 if (!IsIconic(hwndMain)) {
\r
8901 DisplayAClock(hdc, timeRemaining, highlight,
\r
8902 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8904 if (highlight && iconCurrent == iconWhite) {
\r
8905 iconCurrent = iconBlack;
\r
8906 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8907 if (IsIconic(hwndMain)) {
\r
8908 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8911 (void) ReleaseDC(hwndMain, hdc);
\r
8913 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8918 LoadGameTimerRunning()
\r
8920 return loadGameTimerEvent != 0;
\r
8924 StopLoadGameTimer()
\r
8926 if (loadGameTimerEvent == 0) return FALSE;
\r
8927 KillTimer(hwndMain, loadGameTimerEvent);
\r
8928 loadGameTimerEvent = 0;
\r
8933 StartLoadGameTimer(long millisec)
\r
8935 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8936 (UINT) millisec, NULL);
\r
8944 char fileTitle[MSG_SIZ];
\r
8946 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8947 f = OpenFileDialog(hwndMain, "a", defName,
\r
8948 appData.oldSaveStyle ? "gam" : "pgn",
\r
8950 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8952 SaveGame(f, 0, "");
\r
8959 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8961 if (delayedTimerEvent != 0) {
\r
8962 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8963 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8965 KillTimer(hwndMain, delayedTimerEvent);
\r
8966 delayedTimerEvent = 0;
\r
8967 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8968 delayedTimerCallback();
\r
8970 delayedTimerCallback = cb;
\r
8971 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8972 (UINT) millisec, NULL);
\r
8975 DelayedEventCallback
\r
8978 if (delayedTimerEvent) {
\r
8979 return delayedTimerCallback;
\r
8986 CancelDelayedEvent()
\r
8988 if (delayedTimerEvent) {
\r
8989 KillTimer(hwndMain, delayedTimerEvent);
\r
8990 delayedTimerEvent = 0;
\r
8994 DWORD GetWin32Priority(int nice)
\r
8995 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8997 REALTIME_PRIORITY_CLASS 0x00000100
\r
8998 HIGH_PRIORITY_CLASS 0x00000080
\r
8999 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
9000 NORMAL_PRIORITY_CLASS 0x00000020
\r
9001 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
9002 IDLE_PRIORITY_CLASS 0x00000040
\r
9004 if (nice < -15) return 0x00000080;
\r
9005 if (nice < 0) return 0x00008000;
\r
9006 if (nice == 0) return 0x00000020;
\r
9007 if (nice < 15) return 0x00004000;
\r
9008 return 0x00000040;
\r
9011 void RunCommand(char *cmdLine)
\r
9013 /* Now create the child process. */
\r
9014 STARTUPINFO siStartInfo;
\r
9015 PROCESS_INFORMATION piProcInfo;
\r
9017 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9018 siStartInfo.lpReserved = NULL;
\r
9019 siStartInfo.lpDesktop = NULL;
\r
9020 siStartInfo.lpTitle = NULL;
\r
9021 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9022 siStartInfo.cbReserved2 = 0;
\r
9023 siStartInfo.lpReserved2 = NULL;
\r
9024 siStartInfo.hStdInput = NULL;
\r
9025 siStartInfo.hStdOutput = NULL;
\r
9026 siStartInfo.hStdError = NULL;
\r
9028 CreateProcess(NULL,
\r
9029 cmdLine, /* command line */
\r
9030 NULL, /* process security attributes */
\r
9031 NULL, /* primary thread security attrs */
\r
9032 TRUE, /* handles are inherited */
\r
9033 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9034 NULL, /* use parent's environment */
\r
9036 &siStartInfo, /* STARTUPINFO pointer */
\r
9037 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9039 CloseHandle(piProcInfo.hThread);
\r
9042 /* Start a child process running the given program.
\r
9043 The process's standard output can be read from "from", and its
\r
9044 standard input can be written to "to".
\r
9045 Exit with fatal error if anything goes wrong.
\r
9046 Returns an opaque pointer that can be used to destroy the process
\r
9050 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
9052 #define BUFSIZE 4096
\r
9054 HANDLE hChildStdinRd, hChildStdinWr,
\r
9055 hChildStdoutRd, hChildStdoutWr;
\r
9056 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
9057 SECURITY_ATTRIBUTES saAttr;
\r
9059 PROCESS_INFORMATION piProcInfo;
\r
9060 STARTUPINFO siStartInfo;
\r
9062 char buf[MSG_SIZ];
\r
9065 if (appData.debugMode) {
\r
9066 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
9071 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
9072 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
9073 saAttr.bInheritHandle = TRUE;
\r
9074 saAttr.lpSecurityDescriptor = NULL;
\r
9077 * The steps for redirecting child's STDOUT:
\r
9078 * 1. Create anonymous pipe to be STDOUT for child.
\r
9079 * 2. Create a noninheritable duplicate of read handle,
\r
9080 * and close the inheritable read handle.
\r
9083 /* Create a pipe for the child's STDOUT. */
\r
9084 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
9085 return GetLastError();
\r
9088 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
9089 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
9090 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
9091 FALSE, /* not inherited */
\r
9092 DUPLICATE_SAME_ACCESS);
\r
9094 return GetLastError();
\r
9096 CloseHandle(hChildStdoutRd);
\r
9099 * The steps for redirecting child's STDIN:
\r
9100 * 1. Create anonymous pipe to be STDIN for child.
\r
9101 * 2. Create a noninheritable duplicate of write handle,
\r
9102 * and close the inheritable write handle.
\r
9105 /* Create a pipe for the child's STDIN. */
\r
9106 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
9107 return GetLastError();
\r
9110 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
9111 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
9112 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
9113 FALSE, /* not inherited */
\r
9114 DUPLICATE_SAME_ACCESS);
\r
9116 return GetLastError();
\r
9118 CloseHandle(hChildStdinWr);
\r
9120 /* Arrange to (1) look in dir for the child .exe file, and
\r
9121 * (2) have dir be the child's working directory. Interpret
\r
9122 * dir relative to the directory WinBoard loaded from. */
\r
9123 GetCurrentDirectory(MSG_SIZ, buf);
\r
9124 SetCurrentDirectory(installDir);
\r
9125 SetCurrentDirectory(dir);
\r
9127 /* Now create the child process. */
\r
9129 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9130 siStartInfo.lpReserved = NULL;
\r
9131 siStartInfo.lpDesktop = NULL;
\r
9132 siStartInfo.lpTitle = NULL;
\r
9133 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9134 siStartInfo.cbReserved2 = 0;
\r
9135 siStartInfo.lpReserved2 = NULL;
\r
9136 siStartInfo.hStdInput = hChildStdinRd;
\r
9137 siStartInfo.hStdOutput = hChildStdoutWr;
\r
9138 siStartInfo.hStdError = hChildStdoutWr;
\r
9140 fSuccess = CreateProcess(NULL,
\r
9141 cmdLine, /* command line */
\r
9142 NULL, /* process security attributes */
\r
9143 NULL, /* primary thread security attrs */
\r
9144 TRUE, /* handles are inherited */
\r
9145 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9146 NULL, /* use parent's environment */
\r
9148 &siStartInfo, /* STARTUPINFO pointer */
\r
9149 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9151 err = GetLastError();
\r
9152 SetCurrentDirectory(buf); /* return to prev directory */
\r
9157 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
9158 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
9159 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
9162 /* Close the handles we don't need in the parent */
\r
9163 CloseHandle(piProcInfo.hThread);
\r
9164 CloseHandle(hChildStdinRd);
\r
9165 CloseHandle(hChildStdoutWr);
\r
9167 /* Prepare return value */
\r
9168 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9169 cp->kind = CPReal;
\r
9170 cp->hProcess = piProcInfo.hProcess;
\r
9171 cp->pid = piProcInfo.dwProcessId;
\r
9172 cp->hFrom = hChildStdoutRdDup;
\r
9173 cp->hTo = hChildStdinWrDup;
\r
9175 *pr = (void *) cp;
\r
9177 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
9178 2000 where engines sometimes don't see the initial command(s)
\r
9179 from WinBoard and hang. I don't understand how that can happen,
\r
9180 but the Sleep is harmless, so I've put it in. Others have also
\r
9181 reported what may be the same problem, so hopefully this will fix
\r
9182 it for them too. */
\r
9190 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
9192 ChildProc *cp; int result;
\r
9194 cp = (ChildProc *) pr;
\r
9195 if (cp == NULL) return;
\r
9197 switch (cp->kind) {
\r
9199 /* TerminateProcess is considered harmful, so... */
\r
9200 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9201 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9202 /* The following doesn't work because the chess program
\r
9203 doesn't "have the same console" as WinBoard. Maybe
\r
9204 we could arrange for this even though neither WinBoard
\r
9205 nor the chess program uses a console for stdio? */
\r
9206 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9208 /* [AS] Special termination modes for misbehaving programs... */
\r
9209 if( signal == 9 ) {
\r
9210 result = TerminateProcess( cp->hProcess, 0 );
\r
9212 if ( appData.debugMode) {
\r
9213 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9216 else if( signal == 10 ) {
\r
9217 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
9219 if( dw != WAIT_OBJECT_0 ) {
\r
9220 result = TerminateProcess( cp->hProcess, 0 );
\r
9222 if ( appData.debugMode) {
\r
9223 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9229 CloseHandle(cp->hProcess);
\r
9233 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9237 closesocket(cp->sock);
\r
9242 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9243 closesocket(cp->sock);
\r
9244 closesocket(cp->sock2);
\r
9252 InterruptChildProcess(ProcRef pr)
\r
9256 cp = (ChildProc *) pr;
\r
9257 if (cp == NULL) return;
\r
9258 switch (cp->kind) {
\r
9260 /* The following doesn't work because the chess program
\r
9261 doesn't "have the same console" as WinBoard. Maybe
\r
9262 we could arrange for this even though neither WinBoard
\r
9263 nor the chess program uses a console for stdio */
\r
9264 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9269 /* Can't interrupt */
\r
9273 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9280 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9282 char cmdLine[MSG_SIZ];
\r
9284 if (port[0] == NULLCHAR) {
\r
9285 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9287 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9289 return StartChildProcess(cmdLine, "", pr);
\r
9293 /* Code to open TCP sockets */
\r
9296 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9302 struct sockaddr_in sa, mysa;
\r
9303 struct hostent FAR *hp;
\r
9304 unsigned short uport;
\r
9305 WORD wVersionRequested;
\r
9308 /* Initialize socket DLL */
\r
9309 wVersionRequested = MAKEWORD(1, 1);
\r
9310 err = WSAStartup(wVersionRequested, &wsaData);
\r
9311 if (err != 0) return err;
\r
9314 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9315 err = WSAGetLastError();
\r
9320 /* Bind local address using (mostly) don't-care values.
\r
9322 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9323 mysa.sin_family = AF_INET;
\r
9324 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9325 uport = (unsigned short) 0;
\r
9326 mysa.sin_port = htons(uport);
\r
9327 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9328 == SOCKET_ERROR) {
\r
9329 err = WSAGetLastError();
\r
9334 /* Resolve remote host name */
\r
9335 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9336 if (!(hp = gethostbyname(host))) {
\r
9337 unsigned int b0, b1, b2, b3;
\r
9339 err = WSAGetLastError();
\r
9341 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9342 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9343 hp->h_addrtype = AF_INET;
\r
9345 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9346 hp->h_addr_list[0] = (char *) malloc(4);
\r
9347 hp->h_addr_list[0][0] = (char) b0;
\r
9348 hp->h_addr_list[0][1] = (char) b1;
\r
9349 hp->h_addr_list[0][2] = (char) b2;
\r
9350 hp->h_addr_list[0][3] = (char) b3;
\r
9356 sa.sin_family = hp->h_addrtype;
\r
9357 uport = (unsigned short) atoi(port);
\r
9358 sa.sin_port = htons(uport);
\r
9359 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9361 /* Make connection */
\r
9362 if (connect(s, (struct sockaddr *) &sa,
\r
9363 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9364 err = WSAGetLastError();
\r
9369 /* Prepare return value */
\r
9370 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9371 cp->kind = CPSock;
\r
9373 *pr = (ProcRef *) cp;
\r
9379 OpenCommPort(char *name, ProcRef *pr)
\r
9384 char fullname[MSG_SIZ];
\r
9386 if (*name != '\\')
\r
9387 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9389 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9391 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9392 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9393 if (h == (HANDLE) -1) {
\r
9394 return GetLastError();
\r
9398 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9400 /* Accumulate characters until a 100ms pause, then parse */
\r
9401 ct.ReadIntervalTimeout = 100;
\r
9402 ct.ReadTotalTimeoutMultiplier = 0;
\r
9403 ct.ReadTotalTimeoutConstant = 0;
\r
9404 ct.WriteTotalTimeoutMultiplier = 0;
\r
9405 ct.WriteTotalTimeoutConstant = 0;
\r
9406 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9408 /* Prepare return value */
\r
9409 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9410 cp->kind = CPComm;
\r
9413 *pr = (ProcRef *) cp;
\r
9419 OpenLoopback(ProcRef *pr)
\r
9421 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9427 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9432 struct sockaddr_in sa, mysa;
\r
9433 struct hostent FAR *hp;
\r
9434 unsigned short uport;
\r
9435 WORD wVersionRequested;
\r
9438 char stderrPortStr[MSG_SIZ];
\r
9440 /* Initialize socket DLL */
\r
9441 wVersionRequested = MAKEWORD(1, 1);
\r
9442 err = WSAStartup(wVersionRequested, &wsaData);
\r
9443 if (err != 0) return err;
\r
9445 /* Resolve remote host name */
\r
9446 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9447 if (!(hp = gethostbyname(host))) {
\r
9448 unsigned int b0, b1, b2, b3;
\r
9450 err = WSAGetLastError();
\r
9452 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9453 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9454 hp->h_addrtype = AF_INET;
\r
9456 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9457 hp->h_addr_list[0] = (char *) malloc(4);
\r
9458 hp->h_addr_list[0][0] = (char) b0;
\r
9459 hp->h_addr_list[0][1] = (char) b1;
\r
9460 hp->h_addr_list[0][2] = (char) b2;
\r
9461 hp->h_addr_list[0][3] = (char) b3;
\r
9467 sa.sin_family = hp->h_addrtype;
\r
9468 uport = (unsigned short) 514;
\r
9469 sa.sin_port = htons(uport);
\r
9470 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9472 /* Bind local socket to unused "privileged" port address
\r
9474 s = INVALID_SOCKET;
\r
9475 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9476 mysa.sin_family = AF_INET;
\r
9477 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9478 for (fromPort = 1023;; fromPort--) {
\r
9479 if (fromPort < 0) {
\r
9481 return WSAEADDRINUSE;
\r
9483 if (s == INVALID_SOCKET) {
\r
9484 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9485 err = WSAGetLastError();
\r
9490 uport = (unsigned short) fromPort;
\r
9491 mysa.sin_port = htons(uport);
\r
9492 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9493 == SOCKET_ERROR) {
\r
9494 err = WSAGetLastError();
\r
9495 if (err == WSAEADDRINUSE) continue;
\r
9499 if (connect(s, (struct sockaddr *) &sa,
\r
9500 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9501 err = WSAGetLastError();
\r
9502 if (err == WSAEADDRINUSE) {
\r
9513 /* Bind stderr local socket to unused "privileged" port address
\r
9515 s2 = INVALID_SOCKET;
\r
9516 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9517 mysa.sin_family = AF_INET;
\r
9518 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9519 for (fromPort = 1023;; fromPort--) {
\r
9520 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9521 if (fromPort < 0) {
\r
9522 (void) closesocket(s);
\r
9524 return WSAEADDRINUSE;
\r
9526 if (s2 == INVALID_SOCKET) {
\r
9527 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9528 err = WSAGetLastError();
\r
9534 uport = (unsigned short) fromPort;
\r
9535 mysa.sin_port = htons(uport);
\r
9536 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9537 == SOCKET_ERROR) {
\r
9538 err = WSAGetLastError();
\r
9539 if (err == WSAEADDRINUSE) continue;
\r
9540 (void) closesocket(s);
\r
9544 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9545 err = WSAGetLastError();
\r
9546 if (err == WSAEADDRINUSE) {
\r
9548 s2 = INVALID_SOCKET;
\r
9551 (void) closesocket(s);
\r
9552 (void) closesocket(s2);
\r
9558 prevStderrPort = fromPort; // remember port used
\r
9559 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9561 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9562 err = WSAGetLastError();
\r
9563 (void) closesocket(s);
\r
9564 (void) closesocket(s2);
\r
9569 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9570 err = WSAGetLastError();
\r
9571 (void) closesocket(s);
\r
9572 (void) closesocket(s2);
\r
9576 if (*user == NULLCHAR) user = UserName();
\r
9577 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9578 err = WSAGetLastError();
\r
9579 (void) closesocket(s);
\r
9580 (void) closesocket(s2);
\r
9584 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9585 err = WSAGetLastError();
\r
9586 (void) closesocket(s);
\r
9587 (void) closesocket(s2);
\r
9592 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9593 err = WSAGetLastError();
\r
9594 (void) closesocket(s);
\r
9595 (void) closesocket(s2);
\r
9599 (void) closesocket(s2); /* Stop listening */
\r
9601 /* Prepare return value */
\r
9602 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9603 cp->kind = CPRcmd;
\r
9606 *pr = (ProcRef *) cp;
\r
9613 AddInputSource(ProcRef pr, int lineByLine,
\r
9614 InputCallback func, VOIDSTAR closure)
\r
9616 InputSource *is, *is2 = NULL;
\r
9617 ChildProc *cp = (ChildProc *) pr;
\r
9619 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9620 is->lineByLine = lineByLine;
\r
9622 is->closure = closure;
\r
9623 is->second = NULL;
\r
9624 is->next = is->buf;
\r
9625 if (pr == NoProc) {
\r
9626 is->kind = CPReal;
\r
9627 consoleInputSource = is;
\r
9629 is->kind = cp->kind;
\r
9631 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9632 we create all threads suspended so that the is->hThread variable can be
\r
9633 safely assigned, then let the threads start with ResumeThread.
\r
9635 switch (cp->kind) {
\r
9637 is->hFile = cp->hFrom;
\r
9638 cp->hFrom = NULL; /* now owned by InputThread */
\r
9640 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9641 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9645 is->hFile = cp->hFrom;
\r
9646 cp->hFrom = NULL; /* now owned by InputThread */
\r
9648 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9649 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9653 is->sock = cp->sock;
\r
9655 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9656 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9660 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9662 is->sock = cp->sock;
\r
9664 is2->sock = cp->sock2;
\r
9665 is2->second = is2;
\r
9667 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9668 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9670 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9671 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9675 if( is->hThread != NULL ) {
\r
9676 ResumeThread( is->hThread );
\r
9679 if( is2 != NULL && is2->hThread != NULL ) {
\r
9680 ResumeThread( is2->hThread );
\r
9684 return (InputSourceRef) is;
\r
9688 RemoveInputSource(InputSourceRef isr)
\r
9692 is = (InputSource *) isr;
\r
9693 is->hThread = NULL; /* tell thread to stop */
\r
9694 CloseHandle(is->hThread);
\r
9695 if (is->second != NULL) {
\r
9696 is->second->hThread = NULL;
\r
9697 CloseHandle(is->second->hThread);
\r
9701 int no_wrap(char *message, int count)
\r
9703 ConsoleOutput(message, count, FALSE);
\r
9708 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9711 int outCount = SOCKET_ERROR;
\r
9712 ChildProc *cp = (ChildProc *) pr;
\r
9713 static OVERLAPPED ovl;
\r
9714 static int line = 0;
\r
9718 if (appData.noJoin || !appData.useInternalWrap)
\r
9719 return no_wrap(message, count);
\r
9722 int width = get_term_width();
\r
9723 int len = wrap(NULL, message, count, width, &line);
\r
9724 char *msg = malloc(len);
\r
9728 return no_wrap(message, count);
\r
9731 dbgchk = wrap(msg, message, count, width, &line);
\r
9732 if (dbgchk != len && appData.debugMode)
\r
9733 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9734 ConsoleOutput(msg, len, FALSE);
\r
9741 if (ovl.hEvent == NULL) {
\r
9742 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9744 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9746 switch (cp->kind) {
\r
9749 outCount = send(cp->sock, message, count, 0);
\r
9750 if (outCount == SOCKET_ERROR) {
\r
9751 *outError = WSAGetLastError();
\r
9753 *outError = NO_ERROR;
\r
9758 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9759 &dOutCount, NULL)) {
\r
9760 *outError = NO_ERROR;
\r
9761 outCount = (int) dOutCount;
\r
9763 *outError = GetLastError();
\r
9768 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9769 &dOutCount, &ovl);
\r
9770 if (*outError == NO_ERROR) {
\r
9771 outCount = (int) dOutCount;
\r
9781 if(n != 0) Sleep(n);
\r
9785 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9788 /* Ignore delay, not implemented for WinBoard */
\r
9789 return OutputToProcess(pr, message, count, outError);
\r
9794 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9795 char *buf, int count, int error)
\r
9797 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9800 /* see wgamelist.c for Game List functions */
\r
9801 /* see wedittags.c for Edit Tags functions */
\r
9808 char buf[MSG_SIZ];
\r
9811 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9812 f = fopen(buf, "r");
\r
9814 ProcessICSInitScript(f);
\r
9824 StartAnalysisClock()
\r
9826 if (analysisTimerEvent) return;
\r
9827 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9828 (UINT) 2000, NULL);
\r
9832 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9834 highlightInfo.sq[0].x = fromX;
\r
9835 highlightInfo.sq[0].y = fromY;
\r
9836 highlightInfo.sq[1].x = toX;
\r
9837 highlightInfo.sq[1].y = toY;
\r
9843 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9844 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9848 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9850 premoveHighlightInfo.sq[0].x = fromX;
\r
9851 premoveHighlightInfo.sq[0].y = fromY;
\r
9852 premoveHighlightInfo.sq[1].x = toX;
\r
9853 premoveHighlightInfo.sq[1].y = toY;
\r
9857 ClearPremoveHighlights()
\r
9859 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9860 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9864 ShutDownFrontEnd()
\r
9866 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9867 DeleteClipboardTempFiles();
\r
9873 if (IsIconic(hwndMain))
\r
9874 ShowWindow(hwndMain, SW_RESTORE);
\r
9876 SetActiveWindow(hwndMain);
\r
9880 * Prototypes for animation support routines
\r
9882 static void ScreenSquare(int column, int row, POINT * pt);
\r
9883 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9884 POINT frames[], int * nFrames);
\r
9890 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9891 { // [HGM] atomic: animate blast wave
\r
9894 explodeInfo.fromX = fromX;
\r
9895 explodeInfo.fromY = fromY;
\r
9896 explodeInfo.toX = toX;
\r
9897 explodeInfo.toY = toY;
\r
9898 for(i=1; i<4*kFactor; i++) {
\r
9899 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9900 DrawPosition(FALSE, board);
\r
9901 Sleep(appData.animSpeed);
\r
9903 explodeInfo.radius = 0;
\r
9904 DrawPosition(TRUE, board);
\r
9908 AnimateMove(board, fromX, fromY, toX, toY)
\r
9915 ChessSquare piece;
\r
9916 int x = toX, y = toY;
\r
9917 POINT start, finish, mid;
\r
9918 POINT frames[kFactor * 2 + 1];
\r
9921 if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();
\r
9923 if (!appData.animate) return;
\r
9924 if (doingSizing) return;
\r
9925 if (fromY < 0 || fromX < 0) return;
\r
9926 piece = board[fromY][fromX];
\r
9927 if (piece >= EmptySquare) return;
\r
9929 if(killX >= 0) toX = killX, toY = killY; // [HGM] lion: first to kill square
\r
9933 ScreenSquare(fromX, fromY, &start);
\r
9934 ScreenSquare(toX, toY, &finish);
\r
9936 /* All moves except knight jumps move in straight line */
\r
9937 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9938 mid.x = start.x + (finish.x - start.x) / 2;
\r
9939 mid.y = start.y + (finish.y - start.y) / 2;
\r
9941 /* Knight: make straight movement then diagonal */
\r
9942 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9943 mid.x = start.x + (finish.x - start.x) / 2;
\r
9947 mid.y = start.y + (finish.y - start.y) / 2;
\r
9951 /* Don't use as many frames for very short moves */
\r
9952 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9953 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9955 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9957 animInfo.from.x = fromX;
\r
9958 animInfo.from.y = fromY;
\r
9959 animInfo.to.x = toX;
\r
9960 animInfo.to.y = toY;
\r
9961 animInfo.lastpos = start;
\r
9962 animInfo.piece = piece;
\r
9963 for (n = 0; n < nFrames; n++) {
\r
9964 animInfo.pos = frames[n];
\r
9965 DrawPosition(FALSE, NULL);
\r
9966 animInfo.lastpos = animInfo.pos;
\r
9967 Sleep(appData.animSpeed);
\r
9969 animInfo.pos = finish;
\r
9970 DrawPosition(FALSE, NULL);
\r
9972 if(toX != x || toY != y) { fromX = toX; fromY = toY; toX = x; toY = y; goto again; } // second leg
\r
9974 animInfo.piece = EmptySquare;
\r
9975 Explode(board, fromX, fromY, toX, toY);
\r
9978 /* Convert board position to corner of screen rect and color */
\r
9981 ScreenSquare(column, row, pt)
\r
9982 int column; int row; POINT * pt;
\r
9985 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
9986 pt->y = lineGap + row * (squareSize + lineGap) + border;
\r
9988 pt->x = lineGap + column * (squareSize + lineGap) + border;
\r
9989 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
9993 /* Generate a series of frame coords from start->mid->finish.
\r
9994 The movement rate doubles until the half way point is
\r
9995 reached, then halves back down to the final destination,
\r
9996 which gives a nice slow in/out effect. The algorithmn
\r
9997 may seem to generate too many intermediates for short
\r
9998 moves, but remember that the purpose is to attract the
\r
9999 viewers attention to the piece about to be moved and
\r
10000 then to where it ends up. Too few frames would be less
\r
10004 Tween(start, mid, finish, factor, frames, nFrames)
\r
10005 POINT * start; POINT * mid;
\r
10006 POINT * finish; int factor;
\r
10007 POINT frames[]; int * nFrames;
\r
10009 int n, fraction = 1, count = 0;
\r
10011 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
10012 for (n = 0; n < factor; n++)
\r
10014 for (n = 0; n < factor; n++) {
\r
10015 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
10016 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
10018 fraction = fraction / 2;
\r
10022 frames[count] = *mid;
\r
10025 /* Slow out, stepping 1/2, then 1/4, ... */
\r
10027 for (n = 0; n < factor; n++) {
\r
10028 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
10029 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
10031 fraction = fraction * 2;
\r
10033 *nFrames = count;
\r
10037 SettingsPopUp(ChessProgramState *cps)
\r
10038 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
10039 EngineOptionsPopup(savedHwnd, cps);
\r
10042 int flock(int fid, int code)
\r
10044 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
10046 ov.hEvent = NULL;
\r
10048 ov.OffsetHigh = 0;
\r
10050 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
10051 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
10052 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
10053 default: return -1;
\r
10062 static char col[8][20];
\r
10063 COLORREF color = *(COLORREF *) colorVariable[n];
\r
10065 snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
10070 ActivateTheme (int new)
\r
10071 { // Redo initialization of features depending on options that can occur in themes
\r
10073 if(new) InitDrawingColors();
\r
10074 fontBitmapSquareSize = 0; // request creation of new font pieces
\r
10075 InitDrawingSizes(boardSize, 0);
\r
10076 InvalidateRect(hwndMain, NULL, TRUE);
\r