2 * WinBoard.c -- Windows NT front end to XBoard
\r
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
\r
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
\r
8 * 2007, 2008, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
\r
10 * Enhancements Copyright 2005 Alessandro Scotti
\r
12 * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,
\r
13 * which was written and is copyrighted by Wayne Christopher.
\r
15 * The following terms apply to Digital Equipment Corporation's copyright
\r
16 * interest in XBoard:
\r
17 * ------------------------------------------------------------------------
\r
18 * All Rights Reserved
\r
20 * Permission to use, copy, modify, and distribute this software and its
\r
21 * documentation for any purpose and without fee is hereby granted,
\r
22 * provided that the above copyright notice appear in all copies and that
\r
23 * both that copyright notice and this permission notice appear in
\r
24 * supporting documentation, and that the name of Digital not be
\r
25 * used in advertising or publicity pertaining to distribution of the
\r
26 * software without specific, written prior permission.
\r
28 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
\r
29 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
\r
30 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
\r
31 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
\r
32 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
\r
33 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
\r
35 * ------------------------------------------------------------------------
\r
37 * The following terms apply to the enhanced version of XBoard
\r
38 * distributed by the Free Software Foundation:
\r
39 * ------------------------------------------------------------------------
\r
41 * GNU XBoard is free software: you can redistribute it and/or modify
\r
42 * it under the terms of the GNU General Public License as published by
\r
43 * the Free Software Foundation, either version 3 of the License, or (at
\r
44 * your option) any later version.
\r
46 * GNU XBoard is distributed in the hope that it will be useful, but
\r
47 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
48 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
49 * General Public License for more details.
\r
51 * You should have received a copy of the GNU General Public License
\r
52 * along with this program. If not, see http://www.gnu.org/licenses/. *
\r
54 *------------------------------------------------------------------------
\r
55 ** See the file ChangeLog for a revision history. */
\r
59 #include <windows.h>
\r
60 #include <winuser.h>
\r
61 #include <winsock.h>
\r
62 #include <commctrl.h>
\r
68 #include <sys/stat.h>
\r
71 #include <commdlg.h>
\r
73 #include <richedit.h>
\r
74 #include <mmsystem.h>
\r
84 #include "frontend.h"
\r
85 #include "backend.h"
\r
86 #include "winboard.h"
\r
88 #include "wclipbrd.h"
\r
89 #include "woptions.h"
\r
90 #include "wsockerr.h"
\r
91 #include "defaults.h"
\r
95 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
\r
98 void mysrandom(unsigned int seed);
\r
100 extern int whiteFlag, blackFlag;
\r
101 Boolean flipClock = FALSE;
\r
102 extern HANDLE chatHandle[];
\r
103 extern enum ICS_TYPE ics_type;
\r
105 int MySearchPath P((char *installDir, char *name, char *fullname));
\r
106 int MyGetFullPathName P((char *name, char *fullname));
\r
107 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
108 VOID NewVariantPopup(HWND hwnd);
\r
109 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
110 /*char*/int promoChar));
\r
111 void DisplayMove P((int moveNumber));
\r
112 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
\r
113 void ChatPopUp P((char *s));
\r
115 ChessSquare piece;
\r
116 POINT pos; /* window coordinates of current pos */
\r
117 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
118 POINT from; /* board coordinates of the piece's orig pos */
\r
119 POINT to; /* board coordinates of the piece's new pos */
\r
122 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
125 POINT start; /* window coordinates of start pos */
\r
126 POINT pos; /* window coordinates of current pos */
\r
127 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
128 POINT from; /* board coordinates of the piece's orig pos */
\r
132 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };
\r
135 POINT sq[2]; /* board coordinates of from, to squares */
\r
138 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
139 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
140 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
141 static HighlightInfo oldPartnerHighlight = { {{-1, -1}, {-1, -1}} };
\r
143 typedef struct { // [HGM] atomic
\r
144 int fromX, fromY, toX, toY, radius;
\r
147 static ExplodeInfo explodeInfo;
\r
149 /* Window class names */
\r
150 char szAppName[] = "WinBoard";
\r
151 char szConsoleName[] = "WBConsole";
\r
153 /* Title bar text */
\r
154 char szTitle[] = "WinBoard";
\r
155 char szConsoleTitle[] = "I C S Interaction";
\r
158 char *settingsFileName;
\r
159 Boolean saveSettingsOnExit;
\r
160 char installDir[MSG_SIZ];
\r
161 int errorExitStatus;
\r
163 BoardSize boardSize;
\r
164 Boolean chessProgram;
\r
165 //static int boardX, boardY;
\r
166 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
167 int squareSize, lineGap, minorSize, border;
\r
168 static int winW, winH;
\r
169 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
170 static int logoHeight = 0;
\r
171 static char messageText[MESSAGE_TEXT_MAX];
\r
172 static int clockTimerEvent = 0;
\r
173 static int loadGameTimerEvent = 0;
\r
174 static int analysisTimerEvent = 0;
\r
175 static DelayedEventCallback delayedTimerCallback;
\r
176 static int delayedTimerEvent = 0;
\r
177 static int buttonCount = 2;
\r
178 char *icsTextMenuString;
\r
180 char *firstChessProgramNames;
\r
181 char *secondChessProgramNames;
\r
183 #define PALETTESIZE 256
\r
185 HINSTANCE hInst; /* current instance */
\r
186 Boolean alwaysOnTop = FALSE;
\r
188 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
189 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
190 COLORREF markerColor[8] = { 0x00FFFF, 0x0000FF, 0x00FF00, 0xFF0000, 0xFFFF00, 0xFF00FF, 0xFFFFFF, 0x000000 };
\r
192 ColorClass currentColorClass;
\r
194 static HWND savedHwnd;
\r
195 HWND hCommPort = NULL; /* currently open comm port */
\r
196 static HWND hwndPause; /* pause button */
\r
197 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
198 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
199 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
200 explodeBrush, /* [HGM] atomic */
\r
201 markerBrush[8], /* [HGM] markers */
\r
202 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
203 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
204 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
205 static HPEN gridPen = NULL;
\r
206 static HPEN highlightPen = NULL;
\r
207 static HPEN premovePen = NULL;
\r
208 static NPLOGPALETTE pLogPal;
\r
209 static BOOL paletteChanged = FALSE;
\r
210 static HICON iconWhite, iconBlack, iconCurrent;
\r
211 static int doingSizing = FALSE;
\r
212 static int lastSizing = 0;
\r
213 static int prevStderrPort;
\r
214 static HBITMAP userLogo;
\r
216 static HBITMAP liteBackTexture = NULL;
\r
217 static HBITMAP darkBackTexture = NULL;
\r
218 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
219 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
220 static int backTextureSquareSize = 0;
\r
221 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
223 #if __GNUC__ && !defined(_winmajor)
\r
224 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
227 #if defined(_winmajor)
\r
228 #define oldDialog (_winmajor < 4)
\r
230 #define oldDialog 0
\r
234 #define INTERNATIONAL
\r
236 #ifdef INTERNATIONAL
\r
237 # define _(s) T_(s)
\r
243 # define Translate(x, y)
\r
244 # define LoadLanguageFile(s)
\r
247 #ifdef INTERNATIONAL
\r
249 Boolean barbaric; // flag indicating if translation is needed
\r
251 // list of item numbers used in each dialog (used to alter language at run time)
\r
253 #define ABOUTBOX -1 /* not sure why these are needed */
\r
254 #define ABOUTBOX2 -1
\r
256 int dialogItems[][42] = {
\r
257 { ABOUTBOX, IDOK, OPT_MESS, 400 },
\r
258 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed,
\r
259 OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors, IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL },
\r
260 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,
\r
261 OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, IDOK, IDCANCEL },
\r
262 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
263 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
264 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
265 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
266 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
267 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
268 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
269 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
270 { ABOUTBOX2, IDC_ChessBoard },
\r
271 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
272 OPT_GameListClose, IDC_GameListDoFilter },
\r
273 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
274 { DLG_Error, IDOK },
\r
275 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
276 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
277 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
278 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
279 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
280 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
281 { DLG_IndexNumber, IDC_Index },
\r
282 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
283 { DLG_TypeInName, IDOK, IDCANCEL },
\r
284 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
285 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
286 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
287 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
288 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
289 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
290 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
291 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
292 OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove },
\r
293 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
294 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
295 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
296 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
297 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
298 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
299 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
300 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
301 GPB_General, GPB_Alarm, OPT_AutoCreate },
\r
302 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
303 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
304 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
305 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
306 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
307 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
308 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
309 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid },
\r
310 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
311 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
312 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
313 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
314 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
315 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
316 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,
\r
317 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
318 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
319 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
320 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
321 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,
\r
322 OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7,
\r
323 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
324 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
325 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
326 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
327 { DLG_MoveHistory },
\r
328 { DLG_EvalGraph },
\r
329 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
330 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
331 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
332 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
333 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
334 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
335 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
336 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
337 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
341 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];
\r
342 static int lastChecked;
\r
343 static char oldLanguage[MSG_SIZ], *menuText[10][30];
\r
344 extern int tinyLayout;
\r
345 extern char * menuBarText[][10];
\r
348 LoadLanguageFile(char *name)
\r
349 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
351 int i=0, j=0, n=0, k;
\r
354 if(!name || name[0] == NULLCHAR) return;
\r
355 snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension
\r
356 appData.language = oldLanguage;
\r
357 if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on
\r
358 if((f = fopen(buf, "r")) == NULL) return;
\r
359 while((k = fgetc(f)) != EOF) {
\r
360 if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }
\r
361 languageBuf[i] = k;
\r
363 if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {
\r
365 if(p = strstr(languageBuf + n + 1, "\" === \"")) {
\r
366 if(p > languageBuf+n+2 && p+8 < languageBuf+i) {
\r
367 if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }
\r
368 english[j] = languageBuf + n + 1; *p = 0;
\r
369 foreign[j++] = p + 7; languageBuf[i-1] = 0;
\r
370 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);
\r
375 } else if(i > 0 && languageBuf[i-1] == '\\') {
\r
377 case 'n': k = '\n'; break;
\r
378 case 'r': k = '\r'; break;
\r
379 case 't': k = '\t'; break;
\r
381 languageBuf[--i] = k;
\r
386 barbaric = (j != 0);
\r
387 safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );
\r
392 { // return the translation of the given string
\r
393 // efficiency can be improved a lot...
\r
395 static char buf[MSG_SIZ];
\r
396 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
397 if(!barbaric) return s;
\r
398 if(!s) return ""; // sanity
\r
399 while(english[i]) {
\r
400 if(!strcmp(s, english[i])) return foreign[i];
\r
401 if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending
\r
402 snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion
\r
411 Translate(HWND hDlg, int dialogID)
\r
412 { // translate all text items in the given dialog
\r
414 char buf[MSG_SIZ], *s;
\r
415 if(!barbaric) return;
\r
416 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
417 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
418 GetWindowText( hDlg, buf, MSG_SIZ );
\r
420 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
421 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
422 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
423 if(strlen(buf) == 0) continue;
\r
425 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
430 TranslateOneMenu(int i, HMENU subMenu)
\r
433 static MENUITEMINFO info;
\r
435 info.cbSize = sizeof(MENUITEMINFO);
\r
436 info.fMask = MIIM_STATE | MIIM_TYPE;
\r
437 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
439 info.dwTypeData = buf;
\r
440 info.cch = sizeof(buf);
\r
441 GetMenuItemInfo(subMenu, j, TRUE, &info);
\r
443 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );
\r
444 else menuText[i][j] = strdup(buf); // remember original on first change
\r
446 if(buf[0] == NULLCHAR) continue;
\r
447 info.dwTypeData = T_(buf);
\r
448 info.cch = strlen(buf)+1;
\r
449 SetMenuItemInfo(subMenu, j, TRUE, &info);
\r
455 TranslateMenus(int addLanguage)
\r
458 WIN32_FIND_DATA fileData;
\r
460 #define IDM_English 1970
\r
462 HMENU mainMenu = GetMenu(hwndMain);
\r
463 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
464 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
465 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
466 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
467 TranslateOneMenu(i, subMenu);
\r
469 DrawMenuBar(hwndMain);
\r
472 if(!addLanguage) return;
\r
473 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
474 HMENU mainMenu = GetMenu(hwndMain);
\r
475 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
476 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
477 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
478 i = 0; lastChecked = IDM_English;
\r
480 char *p, *q = fileData.cFileName;
\r
481 int checkFlag = MF_UNCHECKED;
\r
482 languageFile[i] = strdup(q);
\r
483 if(barbaric && !strcmp(oldLanguage, q)) {
\r
484 checkFlag = MF_CHECKED;
\r
485 lastChecked = IDM_English + i + 1;
\r
486 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
488 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
489 p = strstr(fileData.cFileName, ".lng");
\r
491 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
492 } while(FindNextFile(hFind, &fileData));
\r
499 #define IDM_RecentEngines 3000
\r
502 RecentEngineMenu (char *s)
\r
504 if(appData.icsActive) return;
\r
505 if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty
\r
506 HMENU mainMenu = GetMenu(hwndMain);
\r
507 HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu
\r
508 int i=IDM_RecentEngines;
\r
509 recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu
\r
510 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
512 char *p = strchr(s, '\n');
\r
513 if(p == NULL) return; // malformed!
\r
515 AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);
\r
529 int cliWidth, cliHeight;
\r
532 SizeInfo sizeInfo[] =
\r
534 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
535 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
536 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
537 { "petite", 33, 1, 1, 1, 0, 0 },
\r
538 { "slim", 37, 2, 1, 0, 0, 0 },
\r
539 { "small", 40, 2, 1, 0, 0, 0 },
\r
540 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
541 { "middling", 49, 2, 0, 0, 0, 0 },
\r
542 { "average", 54, 2, 0, 0, 0, 0 },
\r
543 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
544 { "medium", 64, 3, 0, 0, 0, 0 },
\r
545 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
546 { "large", 80, 3, 0, 0, 0, 0 },
\r
547 { "big", 87, 3, 0, 0, 0, 0 },
\r
548 { "huge", 95, 3, 0, 0, 0, 0 },
\r
549 { "giant", 108, 3, 0, 0, 0, 0 },
\r
550 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
551 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
552 { NULL, 0, 0, 0, 0, 0, 0 }
\r
555 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
556 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
558 { 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
559 { 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
560 { 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
561 { 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
562 { 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
563 { 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
564 { 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
565 { 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
566 { 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
567 { 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
568 { 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
569 { 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
570 { 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
571 { 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
572 { 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
573 { 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
574 { 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
575 { 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
578 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
587 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
588 #define N_BUTTONS 5
\r
590 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
592 {"<<", IDM_ToStart, NULL, NULL},
\r
593 {"<", IDM_Backward, NULL, NULL},
\r
594 {"P", IDM_Pause, NULL, NULL},
\r
595 {">", IDM_Forward, NULL, NULL},
\r
596 {">>", IDM_ToEnd, NULL, NULL},
\r
599 int tinyLayout = 0, smallLayout = 0;
\r
600 #define MENU_BAR_ITEMS 9
\r
601 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
602 { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },
\r
603 { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },
\r
607 MySound sounds[(int)NSoundClasses];
\r
608 MyTextAttribs textAttribs[(int)NColorClasses];
\r
610 MyColorizeAttribs colorizeAttribs[] = {
\r
611 { (COLORREF)0, 0, N_("Shout Text") },
\r
612 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
613 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
614 { (COLORREF)0, 0, N_("Channel Text") },
\r
615 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
616 { (COLORREF)0, 0, N_("Tell Text") },
\r
617 { (COLORREF)0, 0, N_("Challenge Text") },
\r
618 { (COLORREF)0, 0, N_("Request Text") },
\r
619 { (COLORREF)0, 0, N_("Seek Text") },
\r
620 { (COLORREF)0, 0, N_("Normal Text") },
\r
621 { (COLORREF)0, 0, N_("None") }
\r
626 static char *commentTitle;
\r
627 static char *commentText;
\r
628 static int commentIndex;
\r
629 static Boolean editComment = FALSE;
\r
632 char errorTitle[MSG_SIZ];
\r
633 char errorMessage[2*MSG_SIZ];
\r
634 HWND errorDialog = NULL;
\r
635 BOOLEAN moveErrorMessageUp = FALSE;
\r
636 BOOLEAN consoleEcho = TRUE;
\r
637 CHARFORMAT consoleCF;
\r
638 COLORREF consoleBackgroundColor;
\r
640 char *programVersion;
\r
646 typedef int CPKind;
\r
655 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
658 #define INPUT_SOURCE_BUF_SIZE 4096
\r
660 typedef struct _InputSource {
\r
667 char buf[INPUT_SOURCE_BUF_SIZE];
\r
671 InputCallback func;
\r
672 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
676 InputSource *consoleInputSource;
\r
681 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
682 VOID ConsoleCreate();
\r
684 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
685 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
686 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
687 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
689 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
690 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
691 void ParseIcsTextMenu(char *icsTextMenuString);
\r
692 VOID PopUpNameDialog(char firstchar);
\r
693 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
697 int GameListOptions();
\r
699 int dummy; // [HGM] for obsolete args
\r
701 HWND hwndMain = NULL; /* root window*/
\r
702 HWND hwndConsole = NULL;
\r
703 HWND commentDialog = NULL;
\r
704 HWND moveHistoryDialog = NULL;
\r
705 HWND evalGraphDialog = NULL;
\r
706 HWND engineOutputDialog = NULL;
\r
707 HWND gameListDialog = NULL;
\r
708 HWND editTagsDialog = NULL;
\r
710 int commentUp = FALSE;
\r
712 WindowPlacement wpMain;
\r
713 WindowPlacement wpConsole;
\r
714 WindowPlacement wpComment;
\r
715 WindowPlacement wpMoveHistory;
\r
716 WindowPlacement wpEvalGraph;
\r
717 WindowPlacement wpEngineOutput;
\r
718 WindowPlacement wpGameList;
\r
719 WindowPlacement wpTags;
\r
721 VOID EngineOptionsPopup(); // [HGM] settings
\r
723 VOID GothicPopUp(char *title, VariantClass variant);
\r
725 * Setting "frozen" should disable all user input other than deleting
\r
726 * the window. We do this while engines are initializing themselves.
\r
728 static int frozen = 0;
\r
729 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
735 if (frozen) return;
\r
737 hmenu = GetMenu(hwndMain);
\r
738 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
739 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
741 DrawMenuBar(hwndMain);
\r
744 /* Undo a FreezeUI */
\r
750 if (!frozen) return;
\r
752 hmenu = GetMenu(hwndMain);
\r
753 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
754 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
756 DrawMenuBar(hwndMain);
\r
759 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
761 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
767 #define JAWS_ALT_INTERCEPT
\r
768 #define JAWS_KBUP_NAVIGATION
\r
769 #define JAWS_KBDOWN_NAVIGATION
\r
770 #define JAWS_MENU_ITEMS
\r
771 #define JAWS_SILENCE
\r
772 #define JAWS_REPLAY
\r
774 #define JAWS_COPYRIGHT
\r
775 #define JAWS_DELETE(X) X
\r
776 #define SAYMACHINEMOVE()
\r
780 /*---------------------------------------------------------------------------*\
\r
784 \*---------------------------------------------------------------------------*/
\r
787 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
788 LPSTR lpCmdLine, int nCmdShow)
\r
791 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
792 // INITCOMMONCONTROLSEX ex;
\r
796 LoadLibrary("RICHED32.DLL");
\r
797 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
799 if (!InitApplication(hInstance)) {
\r
802 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
809 // InitCommonControlsEx(&ex);
\r
810 InitCommonControls();
\r
812 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
813 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
814 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
816 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
818 while (GetMessage(&msg, /* message structure */
\r
819 NULL, /* handle of window receiving the message */
\r
820 0, /* lowest message to examine */
\r
821 0)) /* highest message to examine */
\r
824 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
825 // [HGM] navigate: switch between all windows with tab
\r
826 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
827 int i, currentElement = 0;
\r
829 // first determine what element of the chain we come from (if any)
\r
830 if(appData.icsActive) {
\r
831 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
832 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
834 if(engineOutputDialog && EngineOutputIsUp()) {
\r
835 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
836 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
838 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
839 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
841 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
842 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
843 if(msg.hwnd == e1) currentElement = 2; else
\r
844 if(msg.hwnd == e2) currentElement = 3; else
\r
845 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
846 if(msg.hwnd == mh) currentElement = 4; else
\r
847 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
848 if(msg.hwnd == hText) currentElement = 5; else
\r
849 if(msg.hwnd == hInput) currentElement = 6; else
\r
850 for (i = 0; i < N_BUTTONS; i++) {
\r
851 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
854 // determine where to go to
\r
855 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
857 currentElement = (currentElement + direction) % 7;
\r
858 switch(currentElement) {
\r
860 h = hwndMain; break; // passing this case always makes the loop exit
\r
862 h = buttonDesc[0].hwnd; break; // could be NULL
\r
864 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
867 if(!EngineOutputIsUp()) continue;
\r
870 if(!MoveHistoryIsUp()) continue;
\r
872 // case 6: // input to eval graph does not seem to get here!
\r
873 // if(!EvalGraphIsUp()) continue;
\r
874 // h = evalGraphDialog; break;
\r
876 if(!appData.icsActive) continue;
\r
880 if(!appData.icsActive) continue;
\r
886 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
887 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
890 continue; // this message now has been processed
\r
894 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
895 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
896 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
897 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
898 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
899 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
900 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
901 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
902 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
903 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
904 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
905 for(i=0; i<MAX_CHAT; i++)
\r
906 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
909 if(done) continue; // [HGM] chat: end patch
\r
910 TranslateMessage(&msg); /* Translates virtual key codes */
\r
911 DispatchMessage(&msg); /* Dispatches message to window */
\r
916 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
919 /*---------------------------------------------------------------------------*\
\r
921 * Initialization functions
\r
923 \*---------------------------------------------------------------------------*/
\r
927 { // update user logo if necessary
\r
928 static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;
\r
930 if(appData.autoLogo) {
\r
931 curName = UserName();
\r
932 if(strcmp(curName, oldUserName)) {
\r
933 GetCurrentDirectory(MSG_SIZ, dir);
\r
934 SetCurrentDirectory(installDir);
\r
935 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
936 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
937 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
938 if(userLogo == NULL)
\r
939 userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
940 SetCurrentDirectory(dir); /* return to prev directory */
\r
946 InitApplication(HINSTANCE hInstance)
\r
950 /* Fill in window class structure with parameters that describe the */
\r
953 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
954 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
955 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
956 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
957 wc.hInstance = hInstance; /* Owner of this class */
\r
958 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
959 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
960 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
961 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
962 wc.lpszClassName = szAppName; /* Name to register as */
\r
964 /* Register the window class and return success/failure code. */
\r
965 if (!RegisterClass(&wc)) return FALSE;
\r
967 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
968 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
970 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
971 wc.hInstance = hInstance;
\r
972 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
973 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
974 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
975 wc.lpszMenuName = NULL;
\r
976 wc.lpszClassName = szConsoleName;
\r
978 if (!RegisterClass(&wc)) return FALSE;
\r
983 /* Set by InitInstance, used by EnsureOnScreen */
\r
984 int screenHeight, screenWidth;
\r
987 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
989 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
990 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
991 if (*x > screenWidth - 32) *x = 0;
\r
992 if (*y > screenHeight - 32) *y = 0;
\r
993 if (*x < minX) *x = minX;
\r
994 if (*y < minY) *y = minY;
\r
998 LoadLogo(ChessProgramState *cps, int n, Boolean ics)
\r
1000 char buf[MSG_SIZ], dir[MSG_SIZ];
\r
1001 GetCurrentDirectory(MSG_SIZ, dir);
\r
1002 SetCurrentDirectory(installDir);
\r
1003 if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {
\r
1004 cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1006 if (cps->programLogo == NULL && appData.debugMode) {
\r
1007 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );
\r
1009 } else if(appData.autoLogo) {
\r
1010 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
\r
1011 char *opponent = "";
\r
1012 if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;
\r
1013 if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;
\r
1014 sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);
\r
1015 if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {
\r
1016 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
1017 cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1020 if(appData.directory[n] && appData.directory[n][0]) {
\r
1021 SetCurrentDirectory(appData.directory[n]);
\r
1022 cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1025 SetCurrentDirectory(dir); /* return to prev directory */
\r
1031 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
1032 backTextureSquareSize = 0; // kludge to force recalculation of texturemode
\r
1034 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1035 if(liteBackTexture) DeleteObject(liteBackTexture);
\r
1036 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1037 liteBackTextureMode = appData.liteBackTextureMode;
\r
1039 if (liteBackTexture == NULL && appData.debugMode) {
\r
1040 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1044 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1045 if(darkBackTexture) DeleteObject(darkBackTexture);
\r
1046 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1047 darkBackTextureMode = appData.darkBackTextureMode;
\r
1049 if (darkBackTexture == NULL && appData.debugMode) {
\r
1050 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1056 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
1058 HWND hwnd; /* Main window handle. */
\r
1060 WINDOWPLACEMENT wp;
\r
1063 hInst = hInstance; /* Store instance handle in our global variable */
\r
1064 programName = szAppName;
\r
1066 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
1067 *filepart = NULLCHAR;
\r
1068 SetCurrentDirectory(installDir);
\r
1070 GetCurrentDirectory(MSG_SIZ, installDir);
\r
1072 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
1073 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
1074 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
1075 /* xboard, and older WinBoards, controlled the move sound with the
\r
1076 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
1077 always turn the option on (so that the backend will call us),
\r
1078 then let the user turn the sound off by setting it to silence if
\r
1079 desired. To accommodate old winboard.ini files saved by old
\r
1080 versions of WinBoard, we also turn off the sound if the option
\r
1081 was initially set to false. [HGM] taken out of InitAppData */
\r
1082 if (!appData.ringBellAfterMoves) {
\r
1083 sounds[(int)SoundMove].name = strdup("");
\r
1084 appData.ringBellAfterMoves = TRUE;
\r
1086 if (appData.debugMode) {
\r
1087 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
1088 setbuf(debugFP, NULL);
\r
1091 LoadLanguageFile(appData.language);
\r
1095 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
1096 // InitEngineUCI( installDir, &second );
\r
1098 /* Create a main window for this application instance. */
\r
1099 hwnd = CreateWindow(szAppName, szTitle,
\r
1100 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
1101 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
1102 NULL, NULL, hInstance, NULL);
\r
1105 /* If window could not be created, return "failure" */
\r
1110 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1111 LoadLogo(&first, 0, FALSE);
\r
1112 LoadLogo(&second, 1, appData.icsActive);
\r
1116 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1117 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1118 iconCurrent = iconWhite;
\r
1119 InitDrawingColors();
\r
1120 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1121 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1122 InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args
\r
1123 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1124 /* Compute window size for each board size, and use the largest
\r
1125 size that fits on this screen as the default. */
\r
1126 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1127 if (boardSize == (BoardSize)-1 &&
\r
1128 winH <= screenHeight
\r
1129 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1130 && winW <= screenWidth) {
\r
1131 boardSize = (BoardSize)ibs;
\r
1135 InitDrawingSizes(boardSize, 0);
\r
1136 RecentEngineMenu(appData.recentEngineList);
\r
1138 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1140 /* [AS] Load textures if specified */
\r
1143 mysrandom( (unsigned) time(NULL) );
\r
1145 /* [AS] Restore layout */
\r
1146 if( wpMoveHistory.visible ) {
\r
1147 MoveHistoryPopUp();
\r
1150 if( wpEvalGraph.visible ) {
\r
1154 if( wpEngineOutput.visible ) {
\r
1155 EngineOutputPopUp();
\r
1158 /* Make the window visible; update its client area; and return "success" */
\r
1159 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1160 wp.length = sizeof(WINDOWPLACEMENT);
\r
1162 wp.showCmd = nCmdShow;
\r
1163 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1164 wp.rcNormalPosition.left = wpMain.x;
\r
1165 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1166 wp.rcNormalPosition.top = wpMain.y;
\r
1167 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1168 SetWindowPlacement(hwndMain, &wp);
\r
1170 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1172 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1173 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1175 if (hwndConsole) {
\r
1177 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1178 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1180 ShowWindow(hwndConsole, nCmdShow);
\r
1181 SetActiveWindow(hwndConsole);
\r
1183 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1184 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1193 HMENU hmenu = GetMenu(hwndMain);
\r
1195 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1196 MF_BYCOMMAND|((appData.icsActive &&
\r
1197 *appData.icsCommPort != NULLCHAR) ?
\r
1198 MF_ENABLED : MF_GRAYED));
\r
1199 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1200 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1201 MF_CHECKED : MF_UNCHECKED));
\r
1204 //---------------------------------------------------------------------------------------------------------
\r
1206 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1207 #define XBOARD FALSE
\r
1209 #define OPTCHAR "/"
\r
1210 #define SEPCHAR "="
\r
1211 #define TOPLEVEL 0
\r
1215 // front-end part of option handling
\r
1218 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1220 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1221 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1224 lf->lfEscapement = 0;
\r
1225 lf->lfOrientation = 0;
\r
1226 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1227 lf->lfItalic = mfp->italic;
\r
1228 lf->lfUnderline = mfp->underline;
\r
1229 lf->lfStrikeOut = mfp->strikeout;
\r
1230 lf->lfCharSet = mfp->charset;
\r
1231 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1232 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1233 lf->lfQuality = DEFAULT_QUALITY;
\r
1234 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1235 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1239 CreateFontInMF(MyFont *mf)
\r
1241 LFfromMFP(&mf->lf, &mf->mfp);
\r
1242 if (mf->hf) DeleteObject(mf->hf);
\r
1243 mf->hf = CreateFontIndirect(&mf->lf);
\r
1246 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1248 colorVariable[] = {
\r
1249 &whitePieceColor,
\r
1250 &blackPieceColor,
\r
1251 &lightSquareColor,
\r
1252 &darkSquareColor,
\r
1253 &highlightSquareColor,
\r
1254 &premoveHighlightColor,
\r
1256 &consoleBackgroundColor,
\r
1257 &appData.fontForeColorWhite,
\r
1258 &appData.fontBackColorWhite,
\r
1259 &appData.fontForeColorBlack,
\r
1260 &appData.fontBackColorBlack,
\r
1261 &appData.evalHistColorWhite,
\r
1262 &appData.evalHistColorBlack,
\r
1263 &appData.highlightArrowColor,
\r
1266 /* Command line font name parser. NULL name means do nothing.
\r
1267 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1268 For backward compatibility, syntax without the colon is also
\r
1269 accepted, but font names with digits in them won't work in that case.
\r
1272 ParseFontName(char *name, MyFontParams *mfp)
\r
1275 if (name == NULL) return;
\r
1277 q = strchr(p, ':');
\r
1279 if (q - p >= sizeof(mfp->faceName))
\r
1280 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1281 memcpy(mfp->faceName, p, q - p);
\r
1282 mfp->faceName[q - p] = NULLCHAR;
\r
1285 q = mfp->faceName;
\r
1287 while (*p && !isdigit(*p)) {
\r
1289 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1290 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1292 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1295 if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);
\r
1296 mfp->pointSize = (float) atof(p);
\r
1297 mfp->bold = (strchr(p, 'b') != NULL);
\r
1298 mfp->italic = (strchr(p, 'i') != NULL);
\r
1299 mfp->underline = (strchr(p, 'u') != NULL);
\r
1300 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1301 mfp->charset = DEFAULT_CHARSET;
\r
1302 q = strchr(p, 'c');
\r
1304 mfp->charset = (BYTE) atoi(q+1);
\r
1308 ParseFont(char *name, int number)
\r
1309 { // wrapper to shield back-end from 'font'
\r
1310 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1315 { // in WB we have a 2D array of fonts; this initializes their description
\r
1317 /* Point font array elements to structures and
\r
1318 parse default font names */
\r
1319 for (i=0; i<NUM_FONTS; i++) {
\r
1320 for (j=0; j<NUM_SIZES; j++) {
\r
1321 font[j][i] = &fontRec[j][i];
\r
1322 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1329 { // here we create the actual fonts from the selected descriptions
\r
1331 for (i=0; i<NUM_FONTS; i++) {
\r
1332 for (j=0; j<NUM_SIZES; j++) {
\r
1333 CreateFontInMF(font[j][i]);
\r
1337 /* Color name parser.
\r
1338 X version accepts X color names, but this one
\r
1339 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1341 ParseColorName(char *name)
\r
1343 int red, green, blue, count;
\r
1344 char buf[MSG_SIZ];
\r
1346 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1348 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1349 &red, &green, &blue);
\r
1352 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1353 DisplayError(buf, 0);
\r
1354 return RGB(0, 0, 0);
\r
1356 return PALETTERGB(red, green, blue);
\r
1360 ParseColor(int n, char *name)
\r
1361 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1362 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1366 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1368 char *e = argValue;
\r
1372 if (*e == 'b') eff |= CFE_BOLD;
\r
1373 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1374 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1375 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1376 else if (*e == '#' || isdigit(*e)) break;
\r
1380 *color = ParseColorName(e);
\r
1384 ParseTextAttribs(ColorClass cc, char *s)
\r
1385 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1386 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1387 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1391 ParseBoardSize(void *addr, char *name)
\r
1392 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1393 BoardSize bs = SizeTiny;
\r
1394 while (sizeInfo[bs].name != NULL) {
\r
1395 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1396 *(BoardSize *)addr = bs;
\r
1401 ExitArgError(_("Unrecognized board size value"), name, TRUE);
\r
1406 { // [HGM] import name from appData first
\r
1409 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1410 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1411 textAttribs[cc].sound.data = NULL;
\r
1412 MyLoadSound(&textAttribs[cc].sound);
\r
1414 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1415 textAttribs[cc].sound.name = strdup("");
\r
1416 textAttribs[cc].sound.data = NULL;
\r
1418 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1419 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1420 sounds[sc].data = NULL;
\r
1421 MyLoadSound(&sounds[sc]);
\r
1426 SetCommPortDefaults()
\r
1428 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1429 dcb.DCBlength = sizeof(DCB);
\r
1430 dcb.BaudRate = 9600;
\r
1431 dcb.fBinary = TRUE;
\r
1432 dcb.fParity = FALSE;
\r
1433 dcb.fOutxCtsFlow = FALSE;
\r
1434 dcb.fOutxDsrFlow = FALSE;
\r
1435 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1436 dcb.fDsrSensitivity = FALSE;
\r
1437 dcb.fTXContinueOnXoff = TRUE;
\r
1438 dcb.fOutX = FALSE;
\r
1440 dcb.fNull = FALSE;
\r
1441 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1442 dcb.fAbortOnError = FALSE;
\r
1444 dcb.Parity = SPACEPARITY;
\r
1445 dcb.StopBits = ONESTOPBIT;
\r
1448 // [HGM] args: these three cases taken out to stay in front-end
\r
1450 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1451 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1452 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1453 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1455 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1456 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1457 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1458 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1459 ad->argName, mfp->faceName, mfp->pointSize,
\r
1460 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1461 mfp->bold ? "b" : "",
\r
1462 mfp->italic ? "i" : "",
\r
1463 mfp->underline ? "u" : "",
\r
1464 mfp->strikeout ? "s" : "",
\r
1465 (int)mfp->charset);
\r
1471 { // [HGM] copy the names from the internal WB variables to appData
\r
1474 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1475 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1476 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1477 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1481 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1482 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1483 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1484 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1485 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1486 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1487 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1488 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1489 (ta->effects) ? " " : "",
\r
1490 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1494 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1495 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1496 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1497 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1498 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1502 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1503 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1504 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1508 ParseCommPortSettings(char *s)
\r
1509 { // wrapper to keep dcb from back-end
\r
1510 ParseCommSettings(s, &dcb);
\r
1515 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1516 GetActualPlacement(hwndMain, &wpMain);
\r
1517 GetActualPlacement(hwndConsole, &wpConsole);
\r
1518 GetActualPlacement(commentDialog, &wpComment);
\r
1519 GetActualPlacement(editTagsDialog, &wpTags);
\r
1520 GetActualPlacement(gameListDialog, &wpGameList);
\r
1521 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1522 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1523 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1527 PrintCommPortSettings(FILE *f, char *name)
\r
1528 { // wrapper to shield back-end from DCB
\r
1529 PrintCommSettings(f, name, &dcb);
\r
1533 MySearchPath(char *installDir, char *name, char *fullname)
\r
1535 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1536 if(name[0]== '%') {
\r
1537 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1538 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1539 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1540 *strchr(buf, '%') = 0;
\r
1541 strcat(fullname, getenv(buf));
\r
1542 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1544 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1545 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1546 return (int) strlen(fullname);
\r
1548 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1552 MyGetFullPathName(char *name, char *fullname)
\r
1555 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1560 { // [HGM] args: allows testing if main window is realized from back-end
\r
1561 return hwndMain != NULL;
\r
1565 PopUpStartupDialog()
\r
1569 LoadLanguageFile(appData.language);
\r
1570 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1571 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1572 FreeProcInstance(lpProc);
\r
1575 /*---------------------------------------------------------------------------*\
\r
1577 * GDI board drawing routines
\r
1579 \*---------------------------------------------------------------------------*/
\r
1581 /* [AS] Draw square using background texture */
\r
1582 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1587 return; /* Should never happen! */
\r
1590 SetGraphicsMode( dst, GM_ADVANCED );
\r
1597 /* X reflection */
\r
1602 x.eDx = (FLOAT) dw + dx - 1;
\r
1605 SetWorldTransform( dst, &x );
\r
1608 /* Y reflection */
\r
1614 x.eDy = (FLOAT) dh + dy - 1;
\r
1616 SetWorldTransform( dst, &x );
\r
1624 x.eDx = (FLOAT) dx;
\r
1625 x.eDy = (FLOAT) dy;
\r
1628 SetWorldTransform( dst, &x );
\r
1632 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1640 SetWorldTransform( dst, &x );
\r
1642 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1645 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1647 PM_WP = (int) WhitePawn,
\r
1648 PM_WN = (int) WhiteKnight,
\r
1649 PM_WB = (int) WhiteBishop,
\r
1650 PM_WR = (int) WhiteRook,
\r
1651 PM_WQ = (int) WhiteQueen,
\r
1652 PM_WF = (int) WhiteFerz,
\r
1653 PM_WW = (int) WhiteWazir,
\r
1654 PM_WE = (int) WhiteAlfil,
\r
1655 PM_WM = (int) WhiteMan,
\r
1656 PM_WO = (int) WhiteCannon,
\r
1657 PM_WU = (int) WhiteUnicorn,
\r
1658 PM_WH = (int) WhiteNightrider,
\r
1659 PM_WA = (int) WhiteAngel,
\r
1660 PM_WC = (int) WhiteMarshall,
\r
1661 PM_WAB = (int) WhiteCardinal,
\r
1662 PM_WD = (int) WhiteDragon,
\r
1663 PM_WL = (int) WhiteLance,
\r
1664 PM_WS = (int) WhiteCobra,
\r
1665 PM_WV = (int) WhiteFalcon,
\r
1666 PM_WSG = (int) WhiteSilver,
\r
1667 PM_WG = (int) WhiteGrasshopper,
\r
1668 PM_WK = (int) WhiteKing,
\r
1669 PM_BP = (int) BlackPawn,
\r
1670 PM_BN = (int) BlackKnight,
\r
1671 PM_BB = (int) BlackBishop,
\r
1672 PM_BR = (int) BlackRook,
\r
1673 PM_BQ = (int) BlackQueen,
\r
1674 PM_BF = (int) BlackFerz,
\r
1675 PM_BW = (int) BlackWazir,
\r
1676 PM_BE = (int) BlackAlfil,
\r
1677 PM_BM = (int) BlackMan,
\r
1678 PM_BO = (int) BlackCannon,
\r
1679 PM_BU = (int) BlackUnicorn,
\r
1680 PM_BH = (int) BlackNightrider,
\r
1681 PM_BA = (int) BlackAngel,
\r
1682 PM_BC = (int) BlackMarshall,
\r
1683 PM_BG = (int) BlackGrasshopper,
\r
1684 PM_BAB = (int) BlackCardinal,
\r
1685 PM_BD = (int) BlackDragon,
\r
1686 PM_BL = (int) BlackLance,
\r
1687 PM_BS = (int) BlackCobra,
\r
1688 PM_BV = (int) BlackFalcon,
\r
1689 PM_BSG = (int) BlackSilver,
\r
1690 PM_BK = (int) BlackKing
\r
1693 static HFONT hPieceFont = NULL;
\r
1694 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1695 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1696 static int fontBitmapSquareSize = 0;
\r
1697 static char pieceToFontChar[(int) EmptySquare] =
\r
1698 { 'p', 'n', 'b', 'r', 'q',
\r
1699 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1700 'k', 'o', 'm', 'v', 't', 'w',
\r
1701 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1704 extern BOOL SetCharTable( char *table, const char * map );
\r
1705 /* [HGM] moved to backend.c */
\r
1707 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1710 BYTE r1 = GetRValue( color );
\r
1711 BYTE g1 = GetGValue( color );
\r
1712 BYTE b1 = GetBValue( color );
\r
1718 /* Create a uniform background first */
\r
1719 hbrush = CreateSolidBrush( color );
\r
1720 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1721 FillRect( hdc, &rc, hbrush );
\r
1722 DeleteObject( hbrush );
\r
1725 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1726 int steps = squareSize / 2;
\r
1729 for( i=0; i<steps; i++ ) {
\r
1730 BYTE r = r1 - (r1-r2) * i / steps;
\r
1731 BYTE g = g1 - (g1-g2) * i / steps;
\r
1732 BYTE b = b1 - (b1-b2) * i / steps;
\r
1734 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1735 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1736 FillRect( hdc, &rc, hbrush );
\r
1737 DeleteObject(hbrush);
\r
1740 else if( mode == 2 ) {
\r
1741 /* Diagonal gradient, good more or less for every piece */
\r
1742 POINT triangle[3];
\r
1743 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1744 HBRUSH hbrush_old;
\r
1745 int steps = squareSize;
\r
1748 triangle[0].x = squareSize - steps;
\r
1749 triangle[0].y = squareSize;
\r
1750 triangle[1].x = squareSize;
\r
1751 triangle[1].y = squareSize;
\r
1752 triangle[2].x = squareSize;
\r
1753 triangle[2].y = squareSize - steps;
\r
1755 for( i=0; i<steps; i++ ) {
\r
1756 BYTE r = r1 - (r1-r2) * i / steps;
\r
1757 BYTE g = g1 - (g1-g2) * i / steps;
\r
1758 BYTE b = b1 - (b1-b2) * i / steps;
\r
1760 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1761 hbrush_old = SelectObject( hdc, hbrush );
\r
1762 Polygon( hdc, triangle, 3 );
\r
1763 SelectObject( hdc, hbrush_old );
\r
1764 DeleteObject(hbrush);
\r
1769 SelectObject( hdc, hpen );
\r
1774 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1775 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1776 piece: follow the steps as explained below.
\r
1778 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1782 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1786 int backColor = whitePieceColor;
\r
1787 int foreColor = blackPieceColor;
\r
1789 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1790 backColor = appData.fontBackColorWhite;
\r
1791 foreColor = appData.fontForeColorWhite;
\r
1793 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1794 backColor = appData.fontBackColorBlack;
\r
1795 foreColor = appData.fontForeColorBlack;
\r
1799 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1801 hbm_old = SelectObject( hdc, hbm );
\r
1805 rc.right = squareSize;
\r
1806 rc.bottom = squareSize;
\r
1808 /* Step 1: background is now black */
\r
1809 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1811 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1813 pt.x = (squareSize - sz.cx) / 2;
\r
1814 pt.y = (squareSize - sz.cy) / 2;
\r
1816 SetBkMode( hdc, TRANSPARENT );
\r
1817 SetTextColor( hdc, chroma );
\r
1818 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1819 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1821 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1822 /* Step 3: the area outside the piece is filled with white */
\r
1823 // FloodFill( hdc, 0, 0, chroma );
\r
1824 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1825 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1826 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1827 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1828 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1830 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1831 but if the start point is not inside the piece we're lost!
\r
1832 There should be a better way to do this... if we could create a region or path
\r
1833 from the fill operation we would be fine for example.
\r
1835 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1836 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1838 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1839 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1840 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1842 SelectObject( dc2, bm2 );
\r
1843 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1844 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1845 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1846 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1847 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1850 DeleteObject( bm2 );
\r
1853 SetTextColor( hdc, 0 );
\r
1855 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1856 draw the piece again in black for safety.
\r
1858 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1860 SelectObject( hdc, hbm_old );
\r
1862 if( hPieceMask[index] != NULL ) {
\r
1863 DeleteObject( hPieceMask[index] );
\r
1866 hPieceMask[index] = hbm;
\r
1869 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1871 SelectObject( hdc, hbm );
\r
1874 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1875 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1876 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1878 SelectObject( dc1, hPieceMask[index] );
\r
1879 SelectObject( dc2, bm2 );
\r
1880 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1881 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1884 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1885 the piece background and deletes (makes transparent) the rest.
\r
1886 Thanks to that mask, we are free to paint the background with the greates
\r
1887 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1888 We use this, to make gradients and give the pieces a "roundish" look.
\r
1890 SetPieceBackground( hdc, backColor, 2 );
\r
1891 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1895 DeleteObject( bm2 );
\r
1898 SetTextColor( hdc, foreColor );
\r
1899 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1901 SelectObject( hdc, hbm_old );
\r
1903 if( hPieceFace[index] != NULL ) {
\r
1904 DeleteObject( hPieceFace[index] );
\r
1907 hPieceFace[index] = hbm;
\r
1910 static int TranslatePieceToFontPiece( int piece )
\r
1940 case BlackMarshall:
\r
1944 case BlackNightrider:
\r
1950 case BlackUnicorn:
\r
1954 case BlackGrasshopper:
\r
1966 case BlackCardinal:
\r
1973 case WhiteMarshall:
\r
1977 case WhiteNightrider:
\r
1983 case WhiteUnicorn:
\r
1987 case WhiteGrasshopper:
\r
1999 case WhiteCardinal:
\r
2008 void CreatePiecesFromFont()
\r
2011 HDC hdc_window = NULL;
\r
2017 if( fontBitmapSquareSize < 0 ) {
\r
2018 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
2022 if( !appData.useFont || appData.renderPiecesWithFont == NULL ||
\r
2023 appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
2024 fontBitmapSquareSize = -1;
\r
2028 if( fontBitmapSquareSize != squareSize ) {
\r
2029 hdc_window = GetDC( hwndMain );
\r
2030 hdc = CreateCompatibleDC( hdc_window );
\r
2032 if( hPieceFont != NULL ) {
\r
2033 DeleteObject( hPieceFont );
\r
2036 for( i=0; i<=(int)BlackKing; i++ ) {
\r
2037 hPieceMask[i] = NULL;
\r
2038 hPieceFace[i] = NULL;
\r
2044 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
2045 fontHeight = appData.fontPieceSize;
\r
2048 fontHeight = (fontHeight * squareSize) / 100;
\r
2050 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2052 lf.lfEscapement = 0;
\r
2053 lf.lfOrientation = 0;
\r
2054 lf.lfWeight = FW_NORMAL;
\r
2056 lf.lfUnderline = 0;
\r
2057 lf.lfStrikeOut = 0;
\r
2058 lf.lfCharSet = DEFAULT_CHARSET;
\r
2059 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2060 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2061 lf.lfQuality = PROOF_QUALITY;
\r
2062 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2063 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2064 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2066 hPieceFont = CreateFontIndirect( &lf );
\r
2068 if( hPieceFont == NULL ) {
\r
2069 fontBitmapSquareSize = -2;
\r
2072 /* Setup font-to-piece character table */
\r
2073 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2074 /* No (or wrong) global settings, try to detect the font */
\r
2075 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2077 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2079 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2080 /* DiagramTT* family */
\r
2081 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2083 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2084 /* Fairy symbols */
\r
2085 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2087 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2088 /* Good Companion (Some characters get warped as literal :-( */
\r
2089 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2090 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2091 SetCharTable(pieceToFontChar, s);
\r
2094 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2095 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2099 /* Create bitmaps */
\r
2100 hfont_old = SelectObject( hdc, hPieceFont );
\r
2101 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2102 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2103 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2105 SelectObject( hdc, hfont_old );
\r
2107 fontBitmapSquareSize = squareSize;
\r
2111 if( hdc != NULL ) {
\r
2115 if( hdc_window != NULL ) {
\r
2116 ReleaseDC( hwndMain, hdc_window );
\r
2121 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2123 char name[128], buf[MSG_SIZ];
\r
2125 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2126 if(appData.pieceDirectory[0]) {
\r
2128 snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);
\r
2129 res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
2130 if(res) return res;
\r
2132 if (gameInfo.event &&
\r
2133 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2134 strcmp(name, "k80s") == 0) {
\r
2135 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2137 return LoadBitmap(hinst, name);
\r
2141 /* Insert a color into the program's logical palette
\r
2142 structure. This code assumes the given color is
\r
2143 the result of the RGB or PALETTERGB macro, and it
\r
2144 knows how those macros work (which is documented).
\r
2147 InsertInPalette(COLORREF color)
\r
2149 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2151 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2152 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2153 pLogPal->palNumEntries--;
\r
2157 pe->peFlags = (char) 0;
\r
2158 pe->peRed = (char) (0xFF & color);
\r
2159 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2160 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2166 InitDrawingColors()
\r
2169 if (pLogPal == NULL) {
\r
2170 /* Allocate enough memory for a logical palette with
\r
2171 * PALETTESIZE entries and set the size and version fields
\r
2172 * of the logical palette structure.
\r
2174 pLogPal = (NPLOGPALETTE)
\r
2175 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2176 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2177 pLogPal->palVersion = 0x300;
\r
2179 pLogPal->palNumEntries = 0;
\r
2181 InsertInPalette(lightSquareColor);
\r
2182 InsertInPalette(darkSquareColor);
\r
2183 InsertInPalette(whitePieceColor);
\r
2184 InsertInPalette(blackPieceColor);
\r
2185 InsertInPalette(highlightSquareColor);
\r
2186 InsertInPalette(premoveHighlightColor);
\r
2188 /* create a logical color palette according the information
\r
2189 * in the LOGPALETTE structure.
\r
2191 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2193 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2194 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2195 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2196 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2197 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2198 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2199 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2200 for(i=0; i<8;i++) markerBrush[i] = CreateSolidBrush(markerColor[i]); // [HGM] markers
\r
2202 /* [AS] Force rendering of the font-based pieces */
\r
2203 if( fontBitmapSquareSize > 0 ) {
\r
2204 fontBitmapSquareSize = 0;
\r
2210 BoardWidth(int boardSize, int n)
\r
2211 { /* [HGM] argument n added to allow different width and height */
\r
2212 int lineGap = sizeInfo[boardSize].lineGap;
\r
2214 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2215 lineGap = appData.overrideLineGap;
\r
2218 return (n + 1) * lineGap +
\r
2219 n * sizeInfo[boardSize].squareSize;
\r
2222 /* Respond to board resize by dragging edge */
\r
2224 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2226 BoardSize newSize = NUM_SIZES - 1;
\r
2227 static int recurse = 0;
\r
2228 if (IsIconic(hwndMain)) return;
\r
2229 if (recurse > 0) return;
\r
2231 while (newSize > 0) {
\r
2232 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2233 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2234 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2237 boardSize = newSize;
\r
2238 InitDrawingSizes(boardSize, flags);
\r
2243 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2246 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2248 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2249 ChessSquare piece;
\r
2250 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2252 SIZE clockSize, messageSize;
\r
2254 char buf[MSG_SIZ];
\r
2256 HMENU hmenu = GetMenu(hwndMain);
\r
2257 RECT crect, wrect, oldRect;
\r
2259 LOGBRUSH logbrush;
\r
2260 VariantClass v = gameInfo.variant;
\r
2262 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2263 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2265 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2266 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2267 if(boardSize == -1) return; // no size defined yet; abort (to allow early call of InitPosition)
\r
2268 oldBoardSize = boardSize;
\r
2270 if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)
\r
2271 { // correct board size to one where built-in pieces exist
\r
2272 if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)
\r
2273 && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range
\r
2274 || (v == VariantShogi && boardSize != SizeModerate) // Japanese-style Shogi
\r
2275 || v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan
\r
2276 || v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy ) {
\r
2277 if(boardSize < SizeMediocre) boardSize = SizePetite; else
\r
2278 if(boardSize > SizeModerate) boardSize = SizeBulky; else
\r
2279 boardSize = SizeMiddling;
\r
2282 if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite
\r
2284 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2285 oldRect.top = wpMain.y;
\r
2286 oldRect.right = wpMain.x + wpMain.width;
\r
2287 oldRect.bottom = wpMain.y + wpMain.height;
\r
2289 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2290 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2291 squareSize = sizeInfo[boardSize].squareSize;
\r
2292 lineGap = sizeInfo[boardSize].lineGap;
\r
2293 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2294 border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;
\r
2296 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2297 lineGap = appData.overrideLineGap;
\r
2300 if (tinyLayout != oldTinyLayout) {
\r
2301 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2303 style &= ~WS_SYSMENU;
\r
2304 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2305 "&Minimize\tCtrl+F4");
\r
2307 style |= WS_SYSMENU;
\r
2308 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2310 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2312 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2313 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2314 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2316 DrawMenuBar(hwndMain);
\r
2319 boardWidth = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;
\r
2320 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;
\r
2322 /* Get text area sizes */
\r
2323 hdc = GetDC(hwndMain);
\r
2324 if (appData.clockMode) {
\r
2325 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2327 snprintf(buf, MSG_SIZ, _("White"));
\r
2329 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2330 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2331 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2332 str = _("We only care about the height here");
\r
2333 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2334 SelectObject(hdc, oldFont);
\r
2335 ReleaseDC(hwndMain, hdc);
\r
2337 /* Compute where everything goes */
\r
2338 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2339 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2340 logoHeight = 2*clockSize.cy;
\r
2341 leftLogoRect.left = OUTER_MARGIN;
\r
2342 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2343 leftLogoRect.top = OUTER_MARGIN;
\r
2344 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2346 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2347 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2348 rightLogoRect.top = OUTER_MARGIN;
\r
2349 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2352 whiteRect.left = leftLogoRect.right;
\r
2353 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2354 whiteRect.top = OUTER_MARGIN;
\r
2355 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2357 blackRect.right = rightLogoRect.left;
\r
2358 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2359 blackRect.top = whiteRect.top;
\r
2360 blackRect.bottom = whiteRect.bottom;
\r
2362 whiteRect.left = OUTER_MARGIN;
\r
2363 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2364 whiteRect.top = OUTER_MARGIN;
\r
2365 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2367 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2368 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2369 blackRect.top = whiteRect.top;
\r
2370 blackRect.bottom = whiteRect.bottom;
\r
2372 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2375 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2376 if (appData.showButtonBar) {
\r
2377 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2378 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2380 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2382 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2383 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2385 boardRect.left = OUTER_MARGIN;
\r
2386 boardRect.right = boardRect.left + boardWidth;
\r
2387 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2388 boardRect.bottom = boardRect.top + boardHeight;
\r
2390 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2391 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2392 oldTinyLayout = tinyLayout;
\r
2393 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2394 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2395 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2396 winW *= 1 + twoBoards;
\r
2397 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2398 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2399 wpMain.height = winH; // without disturbing window attachments
\r
2400 GetWindowRect(hwndMain, &wrect);
\r
2401 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2402 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2404 // [HGM] placement: let attached windows follow size change.
\r
2405 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2406 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2407 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2408 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2409 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2411 /* compensate if menu bar wrapped */
\r
2412 GetClientRect(hwndMain, &crect);
\r
2413 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2414 wpMain.height += offby;
\r
2416 case WMSZ_TOPLEFT:
\r
2417 SetWindowPos(hwndMain, NULL,
\r
2418 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2419 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2422 case WMSZ_TOPRIGHT:
\r
2424 SetWindowPos(hwndMain, NULL,
\r
2425 wrect.left, wrect.bottom - wpMain.height,
\r
2426 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2429 case WMSZ_BOTTOMLEFT:
\r
2431 SetWindowPos(hwndMain, NULL,
\r
2432 wrect.right - wpMain.width, wrect.top,
\r
2433 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2436 case WMSZ_BOTTOMRIGHT:
\r
2440 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2441 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2446 for (i = 0; i < N_BUTTONS; i++) {
\r
2447 if (buttonDesc[i].hwnd != NULL) {
\r
2448 DestroyWindow(buttonDesc[i].hwnd);
\r
2449 buttonDesc[i].hwnd = NULL;
\r
2451 if (appData.showButtonBar) {
\r
2452 buttonDesc[i].hwnd =
\r
2453 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2454 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2455 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2456 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2457 (HMENU) buttonDesc[i].id,
\r
2458 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2460 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2461 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2462 MAKELPARAM(FALSE, 0));
\r
2464 if (buttonDesc[i].id == IDM_Pause)
\r
2465 hwndPause = buttonDesc[i].hwnd;
\r
2466 buttonDesc[i].wndproc = (WNDPROC)
\r
2467 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2470 if (gridPen != NULL) DeleteObject(gridPen);
\r
2471 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2472 if (premovePen != NULL) DeleteObject(premovePen);
\r
2473 if (lineGap != 0) {
\r
2474 logbrush.lbStyle = BS_SOLID;
\r
2475 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2477 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2478 lineGap, &logbrush, 0, NULL);
\r
2479 logbrush.lbColor = highlightSquareColor;
\r
2481 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2482 lineGap, &logbrush, 0, NULL);
\r
2484 logbrush.lbColor = premoveHighlightColor;
\r
2486 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2487 lineGap, &logbrush, 0, NULL);
\r
2489 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2490 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2491 gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;
\r
2492 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2493 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;
\r
2494 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2495 BOARD_WIDTH * (squareSize + lineGap) + border;
\r
2496 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2498 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2499 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;
\r
2500 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2501 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2502 lineGap / 2 + (i * (squareSize + lineGap)) + border;
\r
2503 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2504 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;
\r
2505 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2509 /* [HGM] Licensing requirement */
\r
2511 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2514 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2516 GothicPopUp( "", VariantNormal);
\r
2519 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2521 /* Load piece bitmaps for this board size */
\r
2522 for (i=0; i<=2; i++) {
\r
2523 for (piece = WhitePawn;
\r
2524 (int) piece < (int) BlackPawn;
\r
2525 piece = (ChessSquare) ((int) piece + 1)) {
\r
2526 if (pieceBitmap[i][piece] != NULL)
\r
2527 DeleteObject(pieceBitmap[i][piece]);
\r
2531 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2532 // Orthodox Chess pieces
\r
2533 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2534 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2535 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2536 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2537 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2538 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2539 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2540 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2541 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2542 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2543 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2544 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2545 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2546 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2547 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2548 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2549 // in Shogi, Hijack the unused Queen for Lance
\r
2550 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2551 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2552 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2554 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2555 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2556 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2559 if(squareSize <= 72 && squareSize >= 33) {
\r
2560 /* A & C are available in most sizes now */
\r
2561 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2562 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2563 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2564 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2565 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2566 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2567 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2568 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2569 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2570 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2571 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2572 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2573 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2574 } else { // Smirf-like
\r
2575 if(gameInfo.variant == VariantSChess) {
\r
2576 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2577 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2578 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2580 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2581 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2582 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2585 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2586 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2587 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2588 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2589 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2590 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2591 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2592 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2593 } else { // WinBoard standard
\r
2594 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2595 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2596 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2601 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2602 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2603 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2604 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2605 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2606 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2607 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2608 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2609 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2610 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2611 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2612 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2613 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2614 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2615 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2616 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2617 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2618 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2619 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2620 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2621 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2622 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2623 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2624 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2625 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2626 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2627 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2628 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2629 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2630 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2631 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2633 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2634 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2635 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2636 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2637 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2638 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2639 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2640 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2641 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2642 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2643 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2644 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2645 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2647 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2648 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2649 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2650 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2651 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2652 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2653 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2654 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2655 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2656 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2657 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2658 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2661 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2662 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2663 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2664 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2665 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2666 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2667 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2668 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2669 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2670 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2671 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2672 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2673 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2674 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2675 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2679 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2680 /* special Shogi support in this size */
\r
2681 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2682 for (piece = WhitePawn;
\r
2683 (int) piece < (int) BlackPawn;
\r
2684 piece = (ChessSquare) ((int) piece + 1)) {
\r
2685 if (pieceBitmap[i][piece] != NULL)
\r
2686 DeleteObject(pieceBitmap[i][piece]);
\r
2689 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2690 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2691 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2692 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2693 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2694 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2695 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2696 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2697 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2698 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2699 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2700 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2701 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2702 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2703 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2704 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2705 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2706 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2707 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2708 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2709 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2710 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2711 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2712 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2713 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2714 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2715 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2716 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2717 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2718 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2719 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2720 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2721 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2722 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2723 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2724 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2725 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2726 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2727 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2728 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2729 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2730 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2736 PieceBitmap(ChessSquare p, int kind)
\r
2738 if ((int) p >= (int) BlackPawn)
\r
2739 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2741 return pieceBitmap[kind][(int) p];
\r
2744 /***************************************************************/
\r
2746 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2747 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2749 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2750 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2754 SquareToPos(int row, int column, int * x, int * y)
\r
2757 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
2758 *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;
\r
2760 *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;
\r
2761 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
2766 DrawCoordsOnDC(HDC hdc)
\r
2768 static char files[] = "0123456789012345678901221098765432109876543210";
\r
2769 static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";
\r
2770 char str[2] = { NULLCHAR, NULLCHAR };
\r
2771 int oldMode, oldAlign, x, y, start, i;
\r
2775 if (!appData.showCoords)
\r
2778 start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;
\r
2780 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2781 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2782 oldAlign = GetTextAlign(hdc);
\r
2783 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2785 y = boardRect.top + lineGap;
\r
2786 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2789 SetTextAlign(hdc, TA_RIGHT|TA_TOP);
\r
2790 x += border - lineGap - 4; y += squareSize - 6;
\r
2792 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2793 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2794 str[0] = files[start + i];
\r
2795 ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);
\r
2796 y += squareSize + lineGap;
\r
2799 start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;
\r
2802 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2803 x += -border + 4; y += border - squareSize + 6;
\r
2805 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2806 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2807 str[0] = ranks[start + i];
\r
2808 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2809 x += squareSize + lineGap;
\r
2812 SelectObject(hdc, oldBrush);
\r
2813 SetBkMode(hdc, oldMode);
\r
2814 SetTextAlign(hdc, oldAlign);
\r
2815 SelectObject(hdc, oldFont);
\r
2819 DrawGridOnDC(HDC hdc)
\r
2823 if (lineGap != 0) {
\r
2824 oldPen = SelectObject(hdc, gridPen);
\r
2825 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2826 SelectObject(hdc, oldPen);
\r
2830 #define HIGHLIGHT_PEN 0
\r
2831 #define PREMOVE_PEN 1
\r
2834 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2837 HPEN oldPen, hPen;
\r
2838 if (lineGap == 0) return;
\r
2840 x1 = boardRect.left +
\r
2841 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;
\r
2842 y1 = boardRect.top +
\r
2843 lineGap/2 + y * (squareSize + lineGap) + border;
\r
2845 x1 = boardRect.left +
\r
2846 lineGap/2 + x * (squareSize + lineGap) + border;
\r
2847 y1 = boardRect.top +
\r
2848 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;
\r
2850 hPen = pen ? premovePen : highlightPen;
\r
2851 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2852 MoveToEx(hdc, x1, y1, NULL);
\r
2853 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2854 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2855 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2856 LineTo(hdc, x1, y1);
\r
2857 SelectObject(hdc, oldPen);
\r
2861 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2864 for (i=0; i<2; i++) {
\r
2865 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2866 DrawHighlightOnDC(hdc, TRUE,
\r
2867 h->sq[i].x, h->sq[i].y,
\r
2872 /* Note: sqcolor is used only in monoMode */
\r
2873 /* Note that this code is largely duplicated in woptions.c,
\r
2874 function DrawSampleSquare, so that needs to be updated too */
\r
2876 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2878 HBITMAP oldBitmap;
\r
2882 if (appData.blindfold) return;
\r
2884 /* [AS] Use font-based pieces if needed */
\r
2885 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2886 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2887 CreatePiecesFromFont();
\r
2889 if( fontBitmapSquareSize == squareSize ) {
\r
2890 int index = TranslatePieceToFontPiece(piece);
\r
2892 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2894 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2895 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2899 squareSize, squareSize,
\r
2904 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2906 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2907 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2911 squareSize, squareSize,
\r
2920 if (appData.monoMode) {
\r
2921 SelectObject(tmphdc, PieceBitmap(piece,
\r
2922 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2923 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2924 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2926 HBRUSH xBrush = whitePieceBrush;
\r
2927 tmpSize = squareSize;
\r
2928 if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);
\r
2930 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2931 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2932 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2933 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2934 x += (squareSize - minorSize)>>1;
\r
2935 y += squareSize - minorSize - 2;
\r
2936 tmpSize = minorSize;
\r
2938 if (color || appData.allWhite ) {
\r
2939 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2941 oldBrush = SelectObject(hdc, xBrush);
\r
2942 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2943 if(appData.upsideDown && color==flipView)
\r
2944 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2946 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2947 /* Use black for outline of white pieces */
\r
2948 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2949 if(appData.upsideDown && color==flipView)
\r
2950 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2952 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2953 } else if(appData.pieceDirectory[0]) {
\r
2954 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2955 oldBrush = SelectObject(hdc, xBrush);
\r
2956 if(appData.upsideDown && color==flipView)
\r
2957 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2959 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2960 SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2961 if(appData.upsideDown && color==flipView)
\r
2962 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2964 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2966 /* Use square color for details of black pieces */
\r
2967 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2968 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2969 if(appData.upsideDown && !flipView)
\r
2970 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2972 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2974 SelectObject(hdc, oldBrush);
\r
2975 SelectObject(tmphdc, oldBitmap);
\r
2979 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2980 int GetBackTextureMode( int algo )
\r
2982 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2986 case BACK_TEXTURE_MODE_PLAIN:
\r
2987 result = 1; /* Always use identity map */
\r
2989 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2990 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2998 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2999 to handle redraws cleanly (as random numbers would always be different).
\r
3001 VOID RebuildTextureSquareInfo()
\r
3011 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
3013 if( liteBackTexture != NULL ) {
\r
3014 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3015 lite_w = bi.bmWidth;
\r
3016 lite_h = bi.bmHeight;
\r
3020 if( darkBackTexture != NULL ) {
\r
3021 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3022 dark_w = bi.bmWidth;
\r
3023 dark_h = bi.bmHeight;
\r
3027 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
3028 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
3029 if( (col + row) & 1 ) {
\r
3031 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
3032 if( lite_w >= squareSize*BOARD_WIDTH )
\r
3033 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
3035 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
3036 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
3037 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
3039 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
3040 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
3045 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
3046 if( dark_w >= squareSize*BOARD_WIDTH )
\r
3047 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
3049 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
3050 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
3051 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
3053 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
3054 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
3061 /* [AS] Arrow highlighting support */
\r
3063 static double A_WIDTH = 5; /* Width of arrow body */
\r
3065 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
3066 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
3068 static double Sqr( double x )
\r
3073 static int Round( double x )
\r
3075 return (int) (x + 0.5);
\r
3078 /* Draw an arrow between two points using current settings */
\r
3079 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
3082 double dx, dy, j, k, x, y;
\r
3084 if( d_x == s_x ) {
\r
3085 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3087 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
3090 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
3091 arrow[1].y = d_y - h;
\r
3093 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3094 arrow[2].y = d_y - h;
\r
3099 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3100 arrow[5].y = d_y - h;
\r
3102 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3103 arrow[4].y = d_y - h;
\r
3105 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3108 else if( d_y == s_y ) {
\r
3109 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3112 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3114 arrow[1].x = d_x - w;
\r
3115 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3117 arrow[2].x = d_x - w;
\r
3118 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3123 arrow[5].x = d_x - w;
\r
3124 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3126 arrow[4].x = d_x - w;
\r
3127 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3130 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3133 /* [AS] Needed a lot of paper for this! :-) */
\r
3134 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3135 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3137 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3139 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3144 arrow[0].x = Round(x - j);
\r
3145 arrow[0].y = Round(y + j*dx);
\r
3147 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3148 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3151 x = (double) d_x - k;
\r
3152 y = (double) d_y - k*dy;
\r
3155 x = (double) d_x + k;
\r
3156 y = (double) d_y + k*dy;
\r
3159 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3161 arrow[6].x = Round(x - j);
\r
3162 arrow[6].y = Round(y + j*dx);
\r
3164 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3165 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3167 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3168 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3173 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3174 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3177 Polygon( hdc, arrow, 7 );
\r
3180 /* [AS] Draw an arrow between two squares */
\r
3181 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3183 int s_x, s_y, d_x, d_y;
\r
3190 if( s_col == d_col && s_row == d_row ) {
\r
3194 /* Get source and destination points */
\r
3195 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3196 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3199 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3201 else if( d_y < s_y ) {
\r
3202 d_y += squareSize / 2 + squareSize / 4;
\r
3205 d_y += squareSize / 2;
\r
3209 d_x += squareSize / 2 - squareSize / 4;
\r
3211 else if( d_x < s_x ) {
\r
3212 d_x += squareSize / 2 + squareSize / 4;
\r
3215 d_x += squareSize / 2;
\r
3218 s_x += squareSize / 2;
\r
3219 s_y += squareSize / 2;
\r
3221 /* Adjust width */
\r
3222 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3225 stLB.lbStyle = BS_SOLID;
\r
3226 stLB.lbColor = appData.highlightArrowColor;
\r
3229 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3230 holdpen = SelectObject( hdc, hpen );
\r
3231 hbrush = CreateBrushIndirect( &stLB );
\r
3232 holdbrush = SelectObject( hdc, hbrush );
\r
3234 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3236 SelectObject( hdc, holdpen );
\r
3237 SelectObject( hdc, holdbrush );
\r
3238 DeleteObject( hpen );
\r
3239 DeleteObject( hbrush );
\r
3242 BOOL HasHighlightInfo()
\r
3244 BOOL result = FALSE;
\r
3246 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3247 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3256 BOOL IsDrawArrowEnabled()
\r
3258 BOOL result = FALSE;
\r
3260 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3267 VOID DrawArrowHighlight( HDC hdc )
\r
3269 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3270 DrawArrowBetweenSquares( hdc,
\r
3271 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3272 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3276 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3278 HRGN result = NULL;
\r
3280 if( HasHighlightInfo() ) {
\r
3281 int x1, y1, x2, y2;
\r
3282 int sx, sy, dx, dy;
\r
3284 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3285 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3287 sx = MIN( x1, x2 );
\r
3288 sy = MIN( y1, y2 );
\r
3289 dx = MAX( x1, x2 ) + squareSize;
\r
3290 dy = MAX( y1, y2 ) + squareSize;
\r
3292 result = CreateRectRgn( sx, sy, dx, dy );
\r
3299 Warning: this function modifies the behavior of several other functions.
\r
3301 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3302 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3303 repaint is scattered all over the place, which is not good for features such as
\r
3304 "arrow highlighting" that require a full repaint of the board.
\r
3306 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3307 user interaction, when speed is not so important) but especially to avoid errors
\r
3308 in the displayed graphics.
\r
3310 In such patched places, I always try refer to this function so there is a single
\r
3311 place to maintain knowledge.
\r
3313 To restore the original behavior, just return FALSE unconditionally.
\r
3315 BOOL IsFullRepaintPreferrable()
\r
3317 BOOL result = FALSE;
\r
3319 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3320 /* Arrow may appear on the board */
\r
3328 This function is called by DrawPosition to know whether a full repaint must
\r
3331 Only DrawPosition may directly call this function, which makes use of
\r
3332 some state information. Other function should call DrawPosition specifying
\r
3333 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3335 BOOL DrawPositionNeedsFullRepaint()
\r
3337 BOOL result = FALSE;
\r
3340 Probably a slightly better policy would be to trigger a full repaint
\r
3341 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3342 but animation is fast enough that it's difficult to notice.
\r
3344 if( animInfo.piece == EmptySquare ) {
\r
3345 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3353 static HBITMAP borderBitmap;
\r
3356 DrawBackgroundOnDC(HDC hdc)
\r
3362 static char oldBorder[MSG_SIZ];
\r
3363 int w = 600, h = 600, mode;
\r
3365 if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid
\r
3366 strncpy(oldBorder, appData.border, MSG_SIZ-1);
\r
3367 borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
3369 if(borderBitmap == NULL) { // loading failed, use white
\r
3370 FillRect( hdc, &boardRect, whitePieceBrush );
\r
3373 tmphdc = CreateCompatibleDC(hdc);
\r
3374 hbm = SelectObject(tmphdc, borderBitmap);
\r
3375 if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {
\r
3379 mode = SetStretchBltMode(hdc, COLORONCOLOR);
\r
3380 StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left,
\r
3381 boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3382 SetStretchBltMode(hdc, mode);
\r
3383 SelectObject(tmphdc, hbm);
\r
3388 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3390 int row, column, x, y, square_color, piece_color;
\r
3391 ChessSquare piece;
\r
3393 HDC texture_hdc = NULL;
\r
3395 /* [AS] Initialize background textures if needed */
\r
3396 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3397 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3398 if( backTextureSquareSize != squareSize
\r
3399 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3400 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3401 backTextureSquareSize = squareSize;
\r
3402 RebuildTextureSquareInfo();
\r
3405 texture_hdc = CreateCompatibleDC( hdc );
\r
3408 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3409 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3411 SquareToPos(row, column, &x, &y);
\r
3413 piece = board[row][column];
\r
3415 square_color = ((column + row) % 2) == 1;
\r
3416 if( gameInfo.variant == VariantXiangqi ) {
\r
3417 square_color = !InPalace(row, column);
\r
3418 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3419 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3421 piece_color = (int) piece < (int) BlackPawn;
\r
3424 /* [HGM] holdings file: light square or black */
\r
3425 if(column == BOARD_LEFT-2) {
\r
3426 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3429 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3433 if(column == BOARD_RGHT + 1 ) {
\r
3434 if( row < gameInfo.holdingsSize )
\r
3437 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3441 if(column == BOARD_LEFT-1 ) /* left align */
\r
3442 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3443 else if( column == BOARD_RGHT) /* right align */
\r
3444 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3445 else if( piece == DarkSquare) DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3447 if (appData.monoMode) {
\r
3448 if (piece == EmptySquare) {
\r
3449 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3450 square_color ? WHITENESS : BLACKNESS);
\r
3452 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3455 else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {
\r
3456 /* [AS] Draw the square using a texture bitmap */
\r
3457 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3458 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3459 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3462 squareSize, squareSize,
\r
3465 backTextureSquareInfo[r][c].mode,
\r
3466 backTextureSquareInfo[r][c].x,
\r
3467 backTextureSquareInfo[r][c].y );
\r
3469 SelectObject( texture_hdc, hbm );
\r
3471 if (piece != EmptySquare) {
\r
3472 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3476 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3478 oldBrush = SelectObject(hdc, brush );
\r
3479 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3480 SelectObject(hdc, oldBrush);
\r
3481 if (piece != EmptySquare)
\r
3482 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3487 if( texture_hdc != NULL ) {
\r
3488 DeleteDC( texture_hdc );
\r
3492 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3493 void fputDW(FILE *f, int x)
\r
3495 fputc(x & 255, f);
\r
3496 fputc(x>>8 & 255, f);
\r
3497 fputc(x>>16 & 255, f);
\r
3498 fputc(x>>24 & 255, f);
\r
3501 #define MAX_CLIPS 200 /* more than enough */
\r
3504 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3506 // HBITMAP bufferBitmap;
\r
3511 int w = 100, h = 50;
\r
3513 if(logo == NULL) {
\r
3514 if(!logoHeight) return;
\r
3515 FillRect( hdc, &logoRect, whitePieceBrush );
\r
3517 // GetClientRect(hwndMain, &Rect);
\r
3518 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3519 // Rect.bottom-Rect.top+1);
\r
3520 tmphdc = CreateCompatibleDC(hdc);
\r
3521 hbm = SelectObject(tmphdc, logo);
\r
3522 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3526 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3527 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3528 SelectObject(tmphdc, hbm);
\r
3536 HDC hdc = GetDC(hwndMain);
\r
3537 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3538 if(appData.autoLogo) {
\r
3540 switch(gameMode) { // pick logos based on game mode
\r
3541 case IcsObserving:
\r
3542 whiteLogo = second.programLogo; // ICS logo
\r
3543 blackLogo = second.programLogo;
\r
3546 case IcsPlayingWhite:
\r
3547 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3548 blackLogo = second.programLogo; // ICS logo
\r
3550 case IcsPlayingBlack:
\r
3551 whiteLogo = second.programLogo; // ICS logo
\r
3552 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3554 case TwoMachinesPlay:
\r
3555 if(first.twoMachinesColor[0] == 'b') {
\r
3556 whiteLogo = second.programLogo;
\r
3557 blackLogo = first.programLogo;
\r
3560 case MachinePlaysWhite:
\r
3561 blackLogo = userLogo;
\r
3563 case MachinePlaysBlack:
\r
3564 whiteLogo = userLogo;
\r
3565 blackLogo = first.programLogo;
\r
3568 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3569 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3570 ReleaseDC(hwndMain, hdc);
\r
3575 UpdateLogos(int display)
\r
3576 { // called after loading new engine(s), in tourney or from menu
\r
3577 LoadLogo(&first, 0, FALSE);
\r
3578 LoadLogo(&second, 1, appData.icsActive);
\r
3579 InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos
\r
3580 if(display) DisplayLogos();
\r
3583 static HDC hdcSeek;
\r
3585 // [HGM] seekgraph
\r
3586 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3589 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3590 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3591 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3592 SelectObject( hdcSeek, hp );
\r
3595 // front-end wrapper for drawing functions to do rectangles
\r
3596 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3601 if (hdcSeek == NULL) {
\r
3602 hdcSeek = GetDC(hwndMain);
\r
3603 if (!appData.monoMode) {
\r
3604 SelectPalette(hdcSeek, hPal, FALSE);
\r
3605 RealizePalette(hdcSeek);
\r
3608 hp = SelectObject( hdcSeek, gridPen );
\r
3609 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3610 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3611 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3612 SelectObject( hdcSeek, hp );
\r
3615 // front-end wrapper for putting text in graph
\r
3616 void DrawSeekText(char *buf, int x, int y)
\r
3619 SetBkMode( hdcSeek, TRANSPARENT );
\r
3620 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3621 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3624 void DrawSeekDot(int x, int y, int color)
\r
3626 int square = color & 0x80;
\r
3627 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3628 color == 0 ? markerBrush[1] : color == 1 ? darkSquareBrush : explodeBrush);
\r
3631 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3632 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3634 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3635 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3636 SelectObject(hdcSeek, oldBrush);
\r
3639 void DrawSeekOpen()
\r
3643 void DrawSeekClose()
\r
3648 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3650 static Board lastReq[2], lastDrawn[2];
\r
3651 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3652 static int lastDrawnFlipView = 0;
\r
3653 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3654 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3657 HBITMAP bufferBitmap;
\r
3658 HBITMAP oldBitmap;
\r
3660 HRGN clips[MAX_CLIPS];
\r
3661 ChessSquare dragged_piece = EmptySquare;
\r
3662 int nr = twoBoards*partnerUp;
\r
3664 /* I'm undecided on this - this function figures out whether a full
\r
3665 * repaint is necessary on its own, so there's no real reason to have the
\r
3666 * caller tell it that. I think this can safely be set to FALSE - but
\r
3667 * if we trust the callers not to request full repaints unnessesarily, then
\r
3668 * we could skip some clipping work. In other words, only request a full
\r
3669 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3670 * gamestart and similar) --Hawk
\r
3672 Boolean fullrepaint = repaint;
\r
3674 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3676 if( DrawPositionNeedsFullRepaint() ) {
\r
3677 fullrepaint = TRUE;
\r
3680 if (board == NULL) {
\r
3681 if (!lastReqValid[nr]) {
\r
3684 board = lastReq[nr];
\r
3686 CopyBoard(lastReq[nr], board);
\r
3687 lastReqValid[nr] = 1;
\r
3690 if (doingSizing) {
\r
3694 if (IsIconic(hwndMain)) {
\r
3698 if (hdc == NULL) {
\r
3699 hdc = GetDC(hwndMain);
\r
3700 if (!appData.monoMode) {
\r
3701 SelectPalette(hdc, hPal, FALSE);
\r
3702 RealizePalette(hdc);
\r
3706 releaseDC = FALSE;
\r
3709 /* Create some work-DCs */
\r
3710 hdcmem = CreateCompatibleDC(hdc);
\r
3711 tmphdc = CreateCompatibleDC(hdc);
\r
3713 /* If dragging is in progress, we temporarely remove the piece */
\r
3714 /* [HGM] or temporarily decrease count if stacked */
\r
3715 /* !! Moved to before board compare !! */
\r
3716 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3717 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3718 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3719 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3720 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3722 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3723 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3724 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3726 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3729 /* Figure out which squares need updating by comparing the
\r
3730 * newest board with the last drawn board and checking if
\r
3731 * flipping has changed.
\r
3733 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3734 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3735 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3736 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3737 SquareToPos(row, column, &x, &y);
\r
3738 clips[num_clips++] =
\r
3739 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3743 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3744 for (i=0; i<2; i++) {
\r
3745 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3746 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3747 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3748 lastDrawnHighlight.sq[i].y >= 0) {
\r
3749 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3750 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3751 clips[num_clips++] =
\r
3752 CreateRectRgn(x - lineGap, y - lineGap,
\r
3753 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3755 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3756 SquareToPos(highlightInfo.sq[i].y, highlightInfo.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
3763 for (i=0; i<2; i++) {
\r
3764 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3765 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3766 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3767 lastDrawnPremove.sq[i].y >= 0) {
\r
3768 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3769 lastDrawnPremove.sq[i].x, &x, &y);
\r
3770 clips[num_clips++] =
\r
3771 CreateRectRgn(x - lineGap, y - lineGap,
\r
3772 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3774 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3775 premoveHighlightInfo.sq[i].y >= 0) {
\r
3776 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3777 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3778 clips[num_clips++] =
\r
3779 CreateRectRgn(x - lineGap, y - lineGap,
\r
3780 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3784 } else { // nr == 1
\r
3785 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3786 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3787 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3788 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3789 for (i=0; i<2; i++) {
\r
3790 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3791 partnerHighlightInfo.sq[i].y >= 0) {
\r
3792 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3793 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3794 clips[num_clips++] =
\r
3795 CreateRectRgn(x - lineGap, y - lineGap,
\r
3796 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3798 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3799 oldPartnerHighlight.sq[i].y >= 0) {
\r
3800 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3801 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3802 clips[num_clips++] =
\r
3803 CreateRectRgn(x - lineGap, y - lineGap,
\r
3804 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3809 fullrepaint = TRUE;
\r
3812 /* Create a buffer bitmap - this is the actual bitmap
\r
3813 * being written to. When all the work is done, we can
\r
3814 * copy it to the real DC (the screen). This avoids
\r
3815 * the problems with flickering.
\r
3817 GetClientRect(hwndMain, &Rect);
\r
3818 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3819 Rect.bottom-Rect.top+1);
\r
3820 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3821 if (!appData.monoMode) {
\r
3822 SelectPalette(hdcmem, hPal, FALSE);
\r
3825 /* Create clips for dragging */
\r
3826 if (!fullrepaint) {
\r
3827 if (dragInfo.from.x >= 0) {
\r
3828 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3829 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3831 if (dragInfo.start.x >= 0) {
\r
3832 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3833 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3835 if (dragInfo.pos.x >= 0) {
\r
3836 x = dragInfo.pos.x - squareSize / 2;
\r
3837 y = dragInfo.pos.y - squareSize / 2;
\r
3838 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3840 if (dragInfo.lastpos.x >= 0) {
\r
3841 x = dragInfo.lastpos.x - squareSize / 2;
\r
3842 y = dragInfo.lastpos.y - squareSize / 2;
\r
3843 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3847 /* Are we animating a move?
\r
3849 * - remove the piece from the board (temporarely)
\r
3850 * - calculate the clipping region
\r
3852 if (!fullrepaint) {
\r
3853 if (animInfo.piece != EmptySquare) {
\r
3854 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3855 x = boardRect.left + animInfo.lastpos.x;
\r
3856 y = boardRect.top + animInfo.lastpos.y;
\r
3857 x2 = boardRect.left + animInfo.pos.x;
\r
3858 y2 = boardRect.top + animInfo.pos.y;
\r
3859 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3860 /* Slight kludge. The real problem is that after AnimateMove is
\r
3861 done, the position on the screen does not match lastDrawn.
\r
3862 This currently causes trouble only on e.p. captures in
\r
3863 atomic, where the piece moves to an empty square and then
\r
3864 explodes. The old and new positions both had an empty square
\r
3865 at the destination, but animation has drawn a piece there and
\r
3866 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3867 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3871 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3872 if (num_clips == 0)
\r
3873 fullrepaint = TRUE;
\r
3875 /* Set clipping on the memory DC */
\r
3876 if (!fullrepaint) {
\r
3877 SelectClipRgn(hdcmem, clips[0]);
\r
3878 for (x = 1; x < num_clips; x++) {
\r
3879 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3880 abort(); // this should never ever happen!
\r
3884 /* Do all the drawing to the memory DC */
\r
3885 if(explodeInfo.radius) { // [HGM] atomic
\r
3887 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3888 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3889 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3890 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3891 x += squareSize/2;
\r
3892 y += squareSize/2;
\r
3893 if(!fullrepaint) {
\r
3894 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3895 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3897 DrawGridOnDC(hdcmem);
\r
3898 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3899 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3900 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3901 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3902 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3903 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3904 SelectObject(hdcmem, oldBrush);
\r
3906 if(border) DrawBackgroundOnDC(hdcmem);
\r
3907 DrawGridOnDC(hdcmem);
\r
3908 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3909 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3910 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3912 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3913 oldPartnerHighlight = partnerHighlightInfo;
\r
3915 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3917 if(nr == 0) // [HGM] dual: markers only on left board
\r
3918 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3919 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3920 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3921 HBRUSH oldBrush = SelectObject(hdcmem, markerBrush[marker[row][column]-1]);
\r
3922 SquareToPos(row, column, &x, &y);
\r
3923 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3924 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3925 SelectObject(hdcmem, oldBrush);
\r
3930 if( appData.highlightMoveWithArrow ) {
\r
3931 DrawArrowHighlight(hdcmem);
\r
3934 DrawCoordsOnDC(hdcmem);
\r
3936 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3937 /* to make sure lastDrawn contains what is actually drawn */
\r
3939 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3940 if (dragged_piece != EmptySquare) {
\r
3941 /* [HGM] or restack */
\r
3942 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3943 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3945 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3946 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3947 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3948 x = dragInfo.pos.x - squareSize / 2;
\r
3949 y = dragInfo.pos.y - squareSize / 2;
\r
3950 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
3951 ((int) dragInfo.piece < (int) BlackPawn),
\r
3952 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3955 /* Put the animated piece back into place and draw it */
\r
3956 if (animInfo.piece != EmptySquare) {
\r
3957 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3958 x = boardRect.left + animInfo.pos.x;
\r
3959 y = boardRect.top + animInfo.pos.y;
\r
3960 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3961 ((int) animInfo.piece < (int) BlackPawn),
\r
3962 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3965 /* Release the bufferBitmap by selecting in the old bitmap
\r
3966 * and delete the memory DC
\r
3968 SelectObject(hdcmem, oldBitmap);
\r
3971 /* Set clipping on the target DC */
\r
3972 if (!fullrepaint) {
\r
3973 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3975 GetRgnBox(clips[x], &rect);
\r
3976 DeleteObject(clips[x]);
\r
3977 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3978 rect.right + wpMain.width/2, rect.bottom);
\r
3980 SelectClipRgn(hdc, clips[0]);
\r
3981 for (x = 1; x < num_clips; x++) {
\r
3982 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3983 abort(); // this should never ever happen!
\r
3987 /* Copy the new bitmap onto the screen in one go.
\r
3988 * This way we avoid any flickering
\r
3990 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3991 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3992 boardRect.right - boardRect.left,
\r
3993 boardRect.bottom - boardRect.top,
\r
3994 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3995 if(saveDiagFlag) {
\r
3996 BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData;
\r
3997 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3999 GetObject(bufferBitmap, sizeof(b), &b);
\r
4000 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\r
4001 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
4002 bih.biWidth = b.bmWidth;
\r
4003 bih.biHeight = b.bmHeight;
\r
4005 bih.biBitCount = b.bmBitsPixel;
\r
4006 bih.biCompression = 0;
\r
4007 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
4008 bih.biXPelsPerMeter = 0;
\r
4009 bih.biYPelsPerMeter = 0;
\r
4010 bih.biClrUsed = 0;
\r
4011 bih.biClrImportant = 0;
\r
4012 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
4013 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
4014 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
4015 // fprintf(diagFile, "%8x\n", (int) pData);
\r
4017 wb = b.bmWidthBytes;
\r
4019 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
4020 int k = ((int*) pData)[i];
\r
4021 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4022 if(j >= 16) break;
\r
4024 if(j >= nrColors) nrColors = j+1;
\r
4026 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
4028 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
4029 for(w=0; w<(wb>>2); w+=2) {
\r
4030 int k = ((int*) pData)[(wb*i>>2) + w];
\r
4031 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4032 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
4033 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
4034 pData[p++] = m | j<<4;
\r
4036 while(p&3) pData[p++] = 0;
\r
4039 wb = ((wb+31)>>5)<<2;
\r
4041 // write BITMAPFILEHEADER
\r
4042 fprintf(diagFile, "BM");
\r
4043 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
4044 fputDW(diagFile, 0);
\r
4045 fputDW(diagFile, 0x36 + (fac?64:0));
\r
4046 // write BITMAPINFOHEADER
\r
4047 fputDW(diagFile, 40);
\r
4048 fputDW(diagFile, b.bmWidth);
\r
4049 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
4050 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
4051 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
4052 fputDW(diagFile, 0);
\r
4053 fputDW(diagFile, 0);
\r
4054 fputDW(diagFile, 0);
\r
4055 fputDW(diagFile, 0);
\r
4056 fputDW(diagFile, 0);
\r
4057 fputDW(diagFile, 0);
\r
4058 // write color table
\r
4060 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
4061 // write bitmap data
\r
4062 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
4063 fputc(pData[i], diagFile);
\r
4068 SelectObject(tmphdc, oldBitmap);
\r
4070 /* Massive cleanup */
\r
4071 for (x = 0; x < num_clips; x++)
\r
4072 DeleteObject(clips[x]);
\r
4075 DeleteObject(bufferBitmap);
\r
4078 ReleaseDC(hwndMain, hdc);
\r
4080 if (lastDrawnFlipView != flipView && nr == 0) {
\r
4082 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
4084 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
4087 /* CopyBoard(lastDrawn, board);*/
\r
4088 lastDrawnHighlight = highlightInfo;
\r
4089 lastDrawnPremove = premoveHighlightInfo;
\r
4090 lastDrawnFlipView = flipView;
\r
4091 lastDrawnValid[nr] = 1;
\r
4094 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
4099 saveDiagFlag = 1; diagFile = f;
\r
4100 HDCDrawPosition(NULL, TRUE, NULL);
\r
4108 /*---------------------------------------------------------------------------*\
\r
4109 | CLIENT PAINT PROCEDURE
\r
4110 | This is the main event-handler for the WM_PAINT message.
\r
4112 \*---------------------------------------------------------------------------*/
\r
4114 PaintProc(HWND hwnd)
\r
4120 if((hdc = BeginPaint(hwnd, &ps))) {
\r
4121 if (IsIconic(hwnd)) {
\r
4122 DrawIcon(hdc, 2, 2, iconCurrent);
\r
4124 if (!appData.monoMode) {
\r
4125 SelectPalette(hdc, hPal, FALSE);
\r
4126 RealizePalette(hdc);
\r
4128 HDCDrawPosition(hdc, 1, NULL);
\r
4129 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
4130 flipView = !flipView; partnerUp = !partnerUp;
\r
4131 HDCDrawPosition(hdc, 1, NULL);
\r
4132 flipView = !flipView; partnerUp = !partnerUp;
\r
4135 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
4136 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
4137 ETO_CLIPPED|ETO_OPAQUE,
\r
4138 &messageRect, messageText, strlen(messageText), NULL);
\r
4139 SelectObject(hdc, oldFont);
\r
4140 DisplayBothClocks();
\r
4143 EndPaint(hwnd,&ps);
\r
4151 * If the user selects on a border boundary, return -1; if off the board,
\r
4152 * return -2. Otherwise map the event coordinate to the square.
\r
4153 * The offset boardRect.left or boardRect.top must already have been
\r
4154 * subtracted from x.
\r
4156 int EventToSquare(x, limit)
\r
4161 if (x < lineGap + border)
\r
4163 x -= lineGap + border;
\r
4164 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4166 x /= (squareSize + lineGap);
\r
4178 DropEnable dropEnables[] = {
\r
4179 { 'P', DP_Pawn, N_("Pawn") },
\r
4180 { 'N', DP_Knight, N_("Knight") },
\r
4181 { 'B', DP_Bishop, N_("Bishop") },
\r
4182 { 'R', DP_Rook, N_("Rook") },
\r
4183 { 'Q', DP_Queen, N_("Queen") },
\r
4187 SetupDropMenu(HMENU hmenu)
\r
4189 int i, count, enable;
\r
4191 extern char white_holding[], black_holding[];
\r
4192 char item[MSG_SIZ];
\r
4194 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4195 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4196 dropEnables[i].piece);
\r
4198 while (p && *p++ == dropEnables[i].piece) count++;
\r
4199 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4200 enable = count > 0 || !appData.testLegality
\r
4201 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4202 && !appData.icsActive);
\r
4203 ModifyMenu(hmenu, dropEnables[i].command,
\r
4204 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4205 dropEnables[i].command, item);
\r
4209 void DragPieceBegin(int x, int y, Boolean instantly)
\r
4211 dragInfo.lastpos.x = boardRect.left + x;
\r
4212 dragInfo.lastpos.y = boardRect.top + y;
\r
4213 if(instantly) dragInfo.pos = dragInfo.lastpos;
\r
4214 dragInfo.from.x = fromX;
\r
4215 dragInfo.from.y = fromY;
\r
4216 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4217 dragInfo.start = dragInfo.from;
\r
4218 SetCapture(hwndMain);
\r
4221 void DragPieceEnd(int x, int y)
\r
4224 dragInfo.start.x = dragInfo.start.y = -1;
\r
4225 dragInfo.from = dragInfo.start;
\r
4226 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4229 void ChangeDragPiece(ChessSquare piece)
\r
4231 dragInfo.piece = piece;
\r
4234 /* Event handler for mouse messages */
\r
4236 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4240 static int recursive = 0;
\r
4242 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4245 if (message == WM_MBUTTONUP) {
\r
4246 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4247 to the middle button: we simulate pressing the left button too!
\r
4249 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4250 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4256 pt.x = LOWORD(lParam);
\r
4257 pt.y = HIWORD(lParam);
\r
4258 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4259 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4260 if (!flipView && y >= 0) {
\r
4261 y = BOARD_HEIGHT - 1 - y;
\r
4263 if (flipView && x >= 0) {
\r
4264 x = BOARD_WIDTH - 1 - x;
\r
4267 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4268 controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status
\r
4270 switch (message) {
\r
4271 case WM_LBUTTONDOWN:
\r
4272 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4273 ClockClick(flipClock); break;
\r
4274 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4275 ClockClick(!flipClock); break;
\r
4277 dragInfo.start.x = dragInfo.start.y = -1;
\r
4278 dragInfo.from = dragInfo.start;
\r
4279 if(fromX == -1 && frozen) { // not sure where this is for
\r
4280 fromX = fromY = -1;
\r
4281 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4284 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4285 DrawPosition(TRUE, NULL);
\r
4288 case WM_LBUTTONUP:
\r
4289 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4290 DrawPosition(TRUE, NULL);
\r
4293 case WM_MOUSEMOVE:
\r
4294 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4295 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4296 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4297 if ((appData.animateDragging || appData.highlightDragging)
\r
4298 && (wParam & MK_LBUTTON)
\r
4299 && dragInfo.from.x >= 0)
\r
4301 BOOL full_repaint = FALSE;
\r
4303 if (appData.animateDragging) {
\r
4304 dragInfo.pos = pt;
\r
4306 if (appData.highlightDragging) {
\r
4307 HoverEvent(highlightInfo.sq[1].x, highlightInfo.sq[1].y, x, y);
\r
4308 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4309 full_repaint = TRUE;
\r
4313 DrawPosition( full_repaint, NULL);
\r
4315 dragInfo.lastpos = dragInfo.pos;
\r
4319 case WM_MOUSEWHEEL: // [DM]
\r
4320 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4321 /* Mouse Wheel is being rolled forward
\r
4322 * Play moves forward
\r
4324 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4325 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4326 /* Mouse Wheel is being rolled backward
\r
4327 * Play moves backward
\r
4329 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4330 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4334 case WM_MBUTTONUP:
\r
4335 case WM_RBUTTONUP:
\r
4337 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4340 case WM_MBUTTONDOWN:
\r
4341 case WM_RBUTTONDOWN:
\r
4344 fromX = fromY = -1;
\r
4345 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4346 dragInfo.start.x = dragInfo.start.y = -1;
\r
4347 dragInfo.from = dragInfo.start;
\r
4348 dragInfo.lastpos = dragInfo.pos;
\r
4349 if (appData.highlightDragging) {
\r
4350 ClearHighlights();
\r
4353 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4354 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4355 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4356 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4357 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4361 DrawPosition(TRUE, NULL);
\r
4363 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4366 if (message == WM_MBUTTONDOWN) {
\r
4367 buttonCount = 3; /* even if system didn't think so */
\r
4368 if (wParam & MK_SHIFT)
\r
4369 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4371 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4372 } else { /* message == WM_RBUTTONDOWN */
\r
4373 /* Just have one menu, on the right button. Windows users don't
\r
4374 think to try the middle one, and sometimes other software steals
\r
4375 it, or it doesn't really exist. */
\r
4376 if(gameInfo.variant != VariantShogi)
\r
4377 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4379 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4383 SetCapture(hwndMain);
\r
4386 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4387 SetupDropMenu(hmenu);
\r
4388 MenuPopup(hwnd, pt, hmenu, -1);
\r
4398 /* Preprocess messages for buttons in main window */
\r
4400 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4402 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4405 for (i=0; i<N_BUTTONS; i++) {
\r
4406 if (buttonDesc[i].id == id) break;
\r
4408 if (i == N_BUTTONS) return 0;
\r
4409 switch (message) {
\r
4414 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4415 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4422 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4425 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4426 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4427 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4428 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4430 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4432 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4433 TypeInEvent((char)wParam);
\r
4439 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4442 /* Process messages for Promotion dialog box */
\r
4444 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4448 switch (message) {
\r
4449 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4450 /* Center the dialog over the application window */
\r
4451 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4452 Translate(hDlg, DLG_PromotionKing);
\r
4453 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4454 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4455 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4456 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4457 SW_SHOW : SW_HIDE);
\r
4458 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4459 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4460 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4461 PieceToChar(WhiteAngel) != '~') ||
\r
4462 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4463 PieceToChar(BlackAngel) != '~') ) ?
\r
4464 SW_SHOW : SW_HIDE);
\r
4465 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4466 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4467 PieceToChar(WhiteMarshall) != '~') ||
\r
4468 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4469 PieceToChar(BlackMarshall) != '~') ) ?
\r
4470 SW_SHOW : SW_HIDE);
\r
4471 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4472 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4473 gameInfo.variant != VariantShogi ?
\r
4474 SW_SHOW : SW_HIDE);
\r
4475 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4476 gameInfo.variant != VariantShogi ?
\r
4477 SW_SHOW : SW_HIDE);
\r
4478 if(gameInfo.variant == VariantShogi) {
\r
4479 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4480 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4481 SetWindowText(hDlg, "Promote?");
\r
4483 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4484 gameInfo.variant == VariantSuper ?
\r
4485 SW_SHOW : SW_HIDE);
\r
4488 case WM_COMMAND: /* message: received a command */
\r
4489 switch (LOWORD(wParam)) {
\r
4491 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4492 ClearHighlights();
\r
4493 DrawPosition(FALSE, NULL);
\r
4496 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4499 promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4502 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4503 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4506 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4507 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4509 case PB_Chancellor:
\r
4510 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4512 case PB_Archbishop:
\r
4513 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4516 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);
\r
4521 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4522 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4523 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4524 fromX = fromY = -1;
\r
4525 if (!appData.highlightLastMove) {
\r
4526 ClearHighlights();
\r
4527 DrawPosition(FALSE, NULL);
\r
4534 /* Pop up promotion dialog */
\r
4536 PromotionPopup(HWND hwnd)
\r
4540 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4541 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4542 hwnd, (DLGPROC)lpProc);
\r
4543 FreeProcInstance(lpProc);
\r
4549 DrawPosition(TRUE, NULL);
\r
4550 PromotionPopup(hwndMain);
\r
4554 LoadGameDialog(HWND hwnd, char* title)
\r
4558 char fileTitle[MSG_SIZ];
\r
4559 f = OpenFileDialog(hwnd, "rb", "",
\r
4560 appData.oldSaveStyle ? "gam" : "pgn",
\r
4562 title, &number, fileTitle, NULL);
\r
4564 cmailMsgLoaded = FALSE;
\r
4565 if (number == 0) {
\r
4566 int error = GameListBuild(f);
\r
4568 DisplayError(_("Cannot build game list"), error);
\r
4569 } else if (!ListEmpty(&gameList) &&
\r
4570 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4571 GameListPopUp(f, fileTitle);
\r
4574 GameListDestroy();
\r
4577 LoadGame(f, number, fileTitle, FALSE);
\r
4581 int get_term_width()
\r
4586 HFONT hfont, hold_font;
\r
4591 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4595 // get the text metrics
\r
4596 hdc = GetDC(hText);
\r
4597 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4598 if (consoleCF.dwEffects & CFE_BOLD)
\r
4599 lf.lfWeight = FW_BOLD;
\r
4600 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4601 lf.lfItalic = TRUE;
\r
4602 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4603 lf.lfStrikeOut = TRUE;
\r
4604 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4605 lf.lfUnderline = TRUE;
\r
4606 hfont = CreateFontIndirect(&lf);
\r
4607 hold_font = SelectObject(hdc, hfont);
\r
4608 GetTextMetrics(hdc, &tm);
\r
4609 SelectObject(hdc, hold_font);
\r
4610 DeleteObject(hfont);
\r
4611 ReleaseDC(hText, hdc);
\r
4613 // get the rectangle
\r
4614 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4616 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4619 void UpdateICSWidth(HWND hText)
\r
4621 LONG old_width, new_width;
\r
4623 new_width = get_term_width(hText, FALSE);
\r
4624 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4625 if (new_width != old_width)
\r
4627 ics_update_width(new_width);
\r
4628 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4633 ChangedConsoleFont()
\r
4636 CHARRANGE tmpsel, sel;
\r
4637 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4638 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4639 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4642 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4643 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4644 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4645 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4646 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4647 * size. This was undocumented in the version of MSVC++ that I had
\r
4648 * when I wrote the code, but is apparently documented now.
\r
4650 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4651 cfmt.bCharSet = f->lf.lfCharSet;
\r
4652 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4653 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4654 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4655 /* Why are the following seemingly needed too? */
\r
4656 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4657 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4658 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4660 tmpsel.cpMax = -1; /*999999?*/
\r
4661 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4662 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4663 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4664 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4666 paraf.cbSize = sizeof(paraf);
\r
4667 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4668 paraf.dxStartIndent = 0;
\r
4669 paraf.dxOffset = WRAP_INDENT;
\r
4670 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4671 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4672 UpdateICSWidth(hText);
\r
4675 /*---------------------------------------------------------------------------*\
\r
4677 * Window Proc for main window
\r
4679 \*---------------------------------------------------------------------------*/
\r
4681 /* Process messages for main window, etc. */
\r
4683 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4686 int wmId, wmEvent;
\r
4690 char fileTitle[MSG_SIZ];
\r
4691 static SnapData sd;
\r
4692 static int peek=0;
\r
4694 switch (message) {
\r
4696 case WM_PAINT: /* message: repaint portion of window */
\r
4700 case WM_ERASEBKGND:
\r
4701 if (IsIconic(hwnd)) {
\r
4702 /* Cheat; change the message */
\r
4703 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4705 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4709 case WM_LBUTTONDOWN:
\r
4710 case WM_MBUTTONDOWN:
\r
4711 case WM_RBUTTONDOWN:
\r
4712 case WM_LBUTTONUP:
\r
4713 case WM_MBUTTONUP:
\r
4714 case WM_RBUTTONUP:
\r
4715 case WM_MOUSEMOVE:
\r
4716 case WM_MOUSEWHEEL:
\r
4717 MouseEvent(hwnd, message, wParam, lParam);
\r
4721 if((char)wParam == '\b') {
\r
4722 ForwardEvent(); peek = 0;
\r
4725 JAWS_KBUP_NAVIGATION
\r
4730 if((char)wParam == '\b') {
\r
4731 if(!peek) BackwardEvent(), peek = 1;
\r
4734 JAWS_KBDOWN_NAVIGATION
\r
4740 JAWS_ALT_INTERCEPT
\r
4742 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4743 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4744 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4745 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4747 SendMessage(h, message, wParam, lParam);
\r
4748 } else if(lParam != KF_REPEAT) {
\r
4749 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4750 TypeInEvent((char)wParam);
\r
4751 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4752 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4757 case WM_PALETTECHANGED:
\r
4758 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4760 HDC hdc = GetDC(hwndMain);
\r
4761 SelectPalette(hdc, hPal, TRUE);
\r
4762 nnew = RealizePalette(hdc);
\r
4764 paletteChanged = TRUE;
\r
4766 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4768 ReleaseDC(hwnd, hdc);
\r
4772 case WM_QUERYNEWPALETTE:
\r
4773 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4775 HDC hdc = GetDC(hwndMain);
\r
4776 paletteChanged = FALSE;
\r
4777 SelectPalette(hdc, hPal, FALSE);
\r
4778 nnew = RealizePalette(hdc);
\r
4780 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4782 ReleaseDC(hwnd, hdc);
\r
4787 case WM_COMMAND: /* message: command from application menu */
\r
4788 wmId = LOWORD(wParam);
\r
4789 wmEvent = HIWORD(wParam);
\r
4794 SAY("new game enter a move to play against the computer with white");
\r
4797 case IDM_NewGameFRC:
\r
4798 if( NewGameFRC() == 0 ) {
\r
4803 case IDM_NewVariant:
\r
4804 NewVariantPopup(hwnd);
\r
4807 case IDM_LoadGame:
\r
4808 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4811 case IDM_LoadNextGame:
\r
4815 case IDM_LoadPrevGame:
\r
4819 case IDM_ReloadGame:
\r
4823 case IDM_LoadPosition:
\r
4824 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4825 Reset(FALSE, TRUE);
\r
4828 f = OpenFileDialog(hwnd, "rb", "",
\r
4829 appData.oldSaveStyle ? "pos" : "fen",
\r
4831 _("Load Position from File"), &number, fileTitle, NULL);
\r
4833 LoadPosition(f, number, fileTitle);
\r
4837 case IDM_LoadNextPosition:
\r
4838 ReloadPosition(1);
\r
4841 case IDM_LoadPrevPosition:
\r
4842 ReloadPosition(-1);
\r
4845 case IDM_ReloadPosition:
\r
4846 ReloadPosition(0);
\r
4849 case IDM_SaveGame:
\r
4850 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4851 f = OpenFileDialog(hwnd, "a", defName,
\r
4852 appData.oldSaveStyle ? "gam" : "pgn",
\r
4854 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4856 SaveGame(f, 0, "");
\r
4860 case IDM_SavePosition:
\r
4861 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4862 f = OpenFileDialog(hwnd, "a", defName,
\r
4863 appData.oldSaveStyle ? "pos" : "fen",
\r
4865 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4867 SavePosition(f, 0, "");
\r
4871 case IDM_SaveDiagram:
\r
4872 defName = "diagram";
\r
4873 f = OpenFileDialog(hwnd, "wb", defName,
\r
4876 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
4882 case IDM_CreateBook:
\r
4883 CreateBookEvent();
\r
4886 case IDM_CopyGame:
\r
4887 CopyGameToClipboard();
\r
4890 case IDM_PasteGame:
\r
4891 PasteGameFromClipboard();
\r
4894 case IDM_CopyGameListToClipboard:
\r
4895 CopyGameListToClipboard();
\r
4898 /* [AS] Autodetect FEN or PGN data */
\r
4899 case IDM_PasteAny:
\r
4900 PasteGameOrFENFromClipboard();
\r
4903 /* [AS] Move history */
\r
4904 case IDM_ShowMoveHistory:
\r
4905 if( MoveHistoryIsUp() ) {
\r
4906 MoveHistoryPopDown();
\r
4909 MoveHistoryPopUp();
\r
4913 /* [AS] Eval graph */
\r
4914 case IDM_ShowEvalGraph:
\r
4915 if( EvalGraphIsUp() ) {
\r
4916 EvalGraphPopDown();
\r
4920 SetFocus(hwndMain);
\r
4924 /* [AS] Engine output */
\r
4925 case IDM_ShowEngineOutput:
\r
4926 if( EngineOutputIsUp() ) {
\r
4927 EngineOutputPopDown();
\r
4930 EngineOutputPopUp();
\r
4934 /* [AS] User adjudication */
\r
4935 case IDM_UserAdjudication_White:
\r
4936 UserAdjudicationEvent( +1 );
\r
4939 case IDM_UserAdjudication_Black:
\r
4940 UserAdjudicationEvent( -1 );
\r
4943 case IDM_UserAdjudication_Draw:
\r
4944 UserAdjudicationEvent( 0 );
\r
4947 /* [AS] Game list options dialog */
\r
4948 case IDM_GameListOptions:
\r
4949 GameListOptions();
\r
4956 case IDM_CopyPosition:
\r
4957 CopyFENToClipboard();
\r
4960 case IDM_PastePosition:
\r
4961 PasteFENFromClipboard();
\r
4964 case IDM_MailMove:
\r
4968 case IDM_ReloadCMailMsg:
\r
4969 Reset(TRUE, TRUE);
\r
4970 ReloadCmailMsgEvent(FALSE);
\r
4973 case IDM_Minimize:
\r
4974 ShowWindow(hwnd, SW_MINIMIZE);
\r
4981 case IDM_MachineWhite:
\r
4982 MachineWhiteEvent();
\r
4984 * refresh the tags dialog only if it's visible
\r
4986 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4988 tags = PGNTags(&gameInfo);
\r
4989 TagsPopUp(tags, CmailMsg());
\r
4992 SAY("computer starts playing white");
\r
4995 case IDM_MachineBlack:
\r
4996 MachineBlackEvent();
\r
4998 * refresh the tags dialog only if it's visible
\r
5000 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
5002 tags = PGNTags(&gameInfo);
\r
5003 TagsPopUp(tags, CmailMsg());
\r
5006 SAY("computer starts playing black");
\r
5009 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
5010 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
5013 case IDM_TwoMachines:
\r
5014 TwoMachinesEvent();
\r
5016 * refresh the tags dialog only if it's visible
\r
5018 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
5020 tags = PGNTags(&gameInfo);
\r
5021 TagsPopUp(tags, CmailMsg());
\r
5024 SAY("computer starts playing both sides");
\r
5027 case IDM_AnalysisMode:
\r
5028 if(AnalyzeModeEvent()) {
\r
5029 SAY("analyzing current position");
\r
5033 case IDM_AnalyzeFile:
\r
5034 AnalyzeFileEvent();
\r
5037 case IDM_IcsClient:
\r
5041 case IDM_EditGame:
\r
5042 case IDM_EditGame2:
\r
5047 case IDM_EditPosition:
\r
5048 case IDM_EditPosition2:
\r
5049 EditPositionEvent();
\r
5050 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
5053 case IDM_Training:
\r
5057 case IDM_ShowGameList:
\r
5058 ShowGameListProc();
\r
5061 case IDM_EditProgs1:
\r
5062 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
5065 case IDM_LoadProg1:
\r
5066 LoadEnginePopUp(hwndMain, 0);
\r
5069 case IDM_LoadProg2:
\r
5070 LoadEnginePopUp(hwndMain, 1);
\r
5073 case IDM_EditServers:
\r
5074 EditTagsPopUp(icsNames, &icsNames);
\r
5077 case IDM_EditTags:
\r
5082 case IDM_EditBook:
\r
5086 case IDM_EditComment:
\r
5088 if (commentUp && editComment) {
\r
5091 EditCommentEvent();
\r
5111 case IDM_CallFlag:
\r
5131 case IDM_StopObserving:
\r
5132 StopObservingEvent();
\r
5135 case IDM_StopExamining:
\r
5136 StopExaminingEvent();
\r
5140 UploadGameEvent();
\r
5143 case IDM_TypeInMove:
\r
5144 TypeInEvent('\000');
\r
5147 case IDM_TypeInName:
\r
5148 PopUpNameDialog('\000');
\r
5151 case IDM_Backward:
\r
5153 SetFocus(hwndMain);
\r
5160 SetFocus(hwndMain);
\r
5165 SetFocus(hwndMain);
\r
5170 SetFocus(hwndMain);
\r
5173 case OPT_GameListNext: // [HGM] forward these two accelerators to Game List
\r
5174 case OPT_GameListPrev:
\r
5175 if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);
\r
5179 RevertEvent(FALSE);
\r
5182 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5183 RevertEvent(TRUE);
\r
5186 case IDM_TruncateGame:
\r
5187 TruncateGameEvent();
\r
5194 case IDM_RetractMove:
\r
5195 RetractMoveEvent();
\r
5198 case IDM_FlipView:
\r
5199 flipView = !flipView;
\r
5200 DrawPosition(FALSE, NULL);
\r
5203 case IDM_FlipClock:
\r
5204 flipClock = !flipClock;
\r
5205 DisplayBothClocks();
\r
5209 case IDM_MuteSounds:
\r
5210 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5211 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5212 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5215 case IDM_GeneralOptions:
\r
5216 GeneralOptionsPopup(hwnd);
\r
5217 DrawPosition(TRUE, NULL);
\r
5220 case IDM_BoardOptions:
\r
5221 BoardOptionsPopup(hwnd);
\r
5224 case IDM_ThemeOptions:
\r
5225 ThemeOptionsPopup(hwnd);
\r
5228 case IDM_EnginePlayOptions:
\r
5229 EnginePlayOptionsPopup(hwnd);
\r
5232 case IDM_Engine1Options:
\r
5233 EngineOptionsPopup(hwnd, &first);
\r
5236 case IDM_Engine2Options:
\r
5238 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5239 EngineOptionsPopup(hwnd, &second);
\r
5242 case IDM_OptionsUCI:
\r
5243 UciOptionsPopup(hwnd);
\r
5247 TourneyPopup(hwnd);
\r
5250 case IDM_IcsOptions:
\r
5251 IcsOptionsPopup(hwnd);
\r
5255 FontsOptionsPopup(hwnd);
\r
5259 SoundOptionsPopup(hwnd);
\r
5262 case IDM_CommPort:
\r
5263 CommPortOptionsPopup(hwnd);
\r
5266 case IDM_LoadOptions:
\r
5267 LoadOptionsPopup(hwnd);
\r
5270 case IDM_SaveOptions:
\r
5271 SaveOptionsPopup(hwnd);
\r
5274 case IDM_TimeControl:
\r
5275 TimeControlOptionsPopup(hwnd);
\r
5278 case IDM_SaveSettings:
\r
5279 SaveSettings(settingsFileName);
\r
5282 case IDM_SaveSettingsOnExit:
\r
5283 saveSettingsOnExit = !saveSettingsOnExit;
\r
5284 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5285 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5286 MF_CHECKED : MF_UNCHECKED));
\r
5297 case IDM_AboutGame:
\r
5302 appData.debugMode = !appData.debugMode;
\r
5303 if (appData.debugMode) {
\r
5304 char dir[MSG_SIZ];
\r
5305 GetCurrentDirectory(MSG_SIZ, dir);
\r
5306 SetCurrentDirectory(installDir);
\r
5307 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5308 SetCurrentDirectory(dir);
\r
5309 setbuf(debugFP, NULL);
\r
5316 case IDM_HELPCONTENTS:
\r
5317 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5318 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5319 MessageBox (GetFocus(),
\r
5320 _("Unable to activate help"),
\r
5321 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5325 case IDM_HELPSEARCH:
\r
5326 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\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_HELPHELP:
\r
5335 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5336 MessageBox (GetFocus(),
\r
5337 _("Unable to activate help"),
\r
5338 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5343 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5345 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5346 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5347 FreeProcInstance(lpProc);
\r
5350 case IDM_DirectCommand1:
\r
5351 AskQuestionEvent(_("Direct Command"),
\r
5352 _("Send to chess program:"), "", "1");
\r
5354 case IDM_DirectCommand2:
\r
5355 AskQuestionEvent(_("Direct Command"),
\r
5356 _("Send to second chess program:"), "", "2");
\r
5359 case EP_WhitePawn:
\r
5360 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5361 fromX = fromY = -1;
\r
5364 case EP_WhiteKnight:
\r
5365 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5366 fromX = fromY = -1;
\r
5369 case EP_WhiteBishop:
\r
5370 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5371 fromX = fromY = -1;
\r
5374 case EP_WhiteRook:
\r
5375 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5376 fromX = fromY = -1;
\r
5379 case EP_WhiteQueen:
\r
5380 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5381 fromX = fromY = -1;
\r
5384 case EP_WhiteFerz:
\r
5385 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5386 fromX = fromY = -1;
\r
5389 case EP_WhiteWazir:
\r
5390 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5391 fromX = fromY = -1;
\r
5394 case EP_WhiteAlfil:
\r
5395 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5396 fromX = fromY = -1;
\r
5399 case EP_WhiteCannon:
\r
5400 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5401 fromX = fromY = -1;
\r
5404 case EP_WhiteCardinal:
\r
5405 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5406 fromX = fromY = -1;
\r
5409 case EP_WhiteMarshall:
\r
5410 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5411 fromX = fromY = -1;
\r
5414 case EP_WhiteKing:
\r
5415 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5416 fromX = fromY = -1;
\r
5419 case EP_BlackPawn:
\r
5420 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5421 fromX = fromY = -1;
\r
5424 case EP_BlackKnight:
\r
5425 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5426 fromX = fromY = -1;
\r
5429 case EP_BlackBishop:
\r
5430 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5431 fromX = fromY = -1;
\r
5434 case EP_BlackRook:
\r
5435 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5436 fromX = fromY = -1;
\r
5439 case EP_BlackQueen:
\r
5440 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5441 fromX = fromY = -1;
\r
5444 case EP_BlackFerz:
\r
5445 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5446 fromX = fromY = -1;
\r
5449 case EP_BlackWazir:
\r
5450 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5451 fromX = fromY = -1;
\r
5454 case EP_BlackAlfil:
\r
5455 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5456 fromX = fromY = -1;
\r
5459 case EP_BlackCannon:
\r
5460 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5461 fromX = fromY = -1;
\r
5464 case EP_BlackCardinal:
\r
5465 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5466 fromX = fromY = -1;
\r
5469 case EP_BlackMarshall:
\r
5470 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5471 fromX = fromY = -1;
\r
5474 case EP_BlackKing:
\r
5475 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5476 fromX = fromY = -1;
\r
5479 case EP_EmptySquare:
\r
5480 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5481 fromX = fromY = -1;
\r
5484 case EP_ClearBoard:
\r
5485 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5486 fromX = fromY = -1;
\r
5490 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5491 fromX = fromY = -1;
\r
5495 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5496 fromX = fromY = -1;
\r
5500 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5501 fromX = fromY = -1;
\r
5505 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5506 fromX = fromY = -1;
\r
5510 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5511 fromX = fromY = -1;
\r
5515 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5516 fromX = fromY = -1;
\r
5520 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5521 fromX = fromY = -1;
\r
5525 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5526 fromX = fromY = -1;
\r
5530 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5531 fromX = fromY = -1;
\r
5535 barbaric = 0; appData.language = "";
\r
5536 TranslateMenus(0);
\r
5537 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5538 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5539 lastChecked = wmId;
\r
5543 if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)
\r
5544 RecentEngineEvent(wmId - IDM_RecentEngines);
\r
5546 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5547 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5548 TranslateMenus(0);
\r
5549 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5550 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5551 lastChecked = wmId;
\r
5554 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5560 case CLOCK_TIMER_ID:
\r
5561 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5562 clockTimerEvent = 0;
\r
5563 DecrementClocks(); /* call into back end */
\r
5565 case LOAD_GAME_TIMER_ID:
\r
5566 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5567 loadGameTimerEvent = 0;
\r
5568 AutoPlayGameLoop(); /* call into back end */
\r
5570 case ANALYSIS_TIMER_ID:
\r
5571 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5572 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5573 AnalysisPeriodicEvent(0);
\r
5575 KillTimer(hwnd, analysisTimerEvent);
\r
5576 analysisTimerEvent = 0;
\r
5579 case DELAYED_TIMER_ID:
\r
5580 KillTimer(hwnd, delayedTimerEvent);
\r
5581 delayedTimerEvent = 0;
\r
5582 delayedTimerCallback();
\r
5587 case WM_USER_Input:
\r
5588 InputEvent(hwnd, message, wParam, lParam);
\r
5591 /* [AS] Also move "attached" child windows */
\r
5592 case WM_WINDOWPOSCHANGING:
\r
5594 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5595 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5597 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5598 /* Window is moving */
\r
5601 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5602 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5603 rcMain.right = wpMain.x + wpMain.width;
\r
5604 rcMain.top = wpMain.y;
\r
5605 rcMain.bottom = wpMain.y + wpMain.height;
\r
5607 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5608 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5609 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5610 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5611 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5612 wpMain.x = lpwp->x;
\r
5613 wpMain.y = lpwp->y;
\r
5618 /* [AS] Snapping */
\r
5619 case WM_ENTERSIZEMOVE:
\r
5620 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5621 if (hwnd == hwndMain) {
\r
5622 doingSizing = TRUE;
\r
5625 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5629 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5630 if (hwnd == hwndMain) {
\r
5631 lastSizing = wParam;
\r
5636 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5637 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5639 case WM_EXITSIZEMOVE:
\r
5640 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5641 if (hwnd == hwndMain) {
\r
5643 doingSizing = FALSE;
\r
5644 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5645 GetClientRect(hwnd, &client);
\r
5646 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5648 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5650 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5653 case WM_DESTROY: /* message: window being destroyed */
\r
5654 PostQuitMessage(0);
\r
5658 if (hwnd == hwndMain) {
\r
5663 default: /* Passes it on if unprocessed */
\r
5664 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5669 /*---------------------------------------------------------------------------*\
\r
5671 * Misc utility routines
\r
5673 \*---------------------------------------------------------------------------*/
\r
5676 * Decent random number generator, at least not as bad as Windows
\r
5677 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5679 unsigned int randstate;
\r
5684 randstate = randstate * 1664525 + 1013904223;
\r
5685 return (int) randstate & 0x7fffffff;
\r
5689 mysrandom(unsigned int seed)
\r
5696 * returns TRUE if user selects a different color, FALSE otherwise
\r
5700 ChangeColor(HWND hwnd, COLORREF *which)
\r
5702 static BOOL firstTime = TRUE;
\r
5703 static DWORD customColors[16];
\r
5705 COLORREF newcolor;
\r
5710 /* Make initial colors in use available as custom colors */
\r
5711 /* Should we put the compiled-in defaults here instead? */
\r
5713 customColors[i++] = lightSquareColor & 0xffffff;
\r
5714 customColors[i++] = darkSquareColor & 0xffffff;
\r
5715 customColors[i++] = whitePieceColor & 0xffffff;
\r
5716 customColors[i++] = blackPieceColor & 0xffffff;
\r
5717 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5718 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5720 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5721 customColors[i++] = textAttribs[ccl].color;
\r
5723 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5724 firstTime = FALSE;
\r
5727 cc.lStructSize = sizeof(cc);
\r
5728 cc.hwndOwner = hwnd;
\r
5729 cc.hInstance = NULL;
\r
5730 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5731 cc.lpCustColors = (LPDWORD) customColors;
\r
5732 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5734 if (!ChooseColor(&cc)) return FALSE;
\r
5736 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5737 if (newcolor == *which) return FALSE;
\r
5738 *which = newcolor;
\r
5742 InitDrawingColors();
\r
5743 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5748 MyLoadSound(MySound *ms)
\r
5754 if (ms->data && ms->flag) free(ms->data);
\r
5757 switch (ms->name[0]) {
\r
5763 /* System sound from Control Panel. Don't preload here. */
\r
5767 if (ms->name[1] == NULLCHAR) {
\r
5768 /* "!" alone = silence */
\r
5771 /* Builtin wave resource. Error if not found. */
\r
5772 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5773 if (h == NULL) break;
\r
5774 ms->data = (void *)LoadResource(hInst, h);
\r
5775 ms->flag = 0; // not maloced, so cannot be freed!
\r
5776 if (h == NULL) break;
\r
5781 /* .wav file. Error if not found. */
\r
5782 f = fopen(ms->name, "rb");
\r
5783 if (f == NULL) break;
\r
5784 if (fstat(fileno(f), &st) < 0) break;
\r
5785 ms->data = malloc(st.st_size);
\r
5787 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5793 char buf[MSG_SIZ];
\r
5794 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5795 DisplayError(buf, GetLastError());
\r
5801 MyPlaySound(MySound *ms)
\r
5803 BOOLEAN ok = FALSE;
\r
5805 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5806 switch (ms->name[0]) {
\r
5808 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5813 /* System sound from Control Panel (deprecated feature).
\r
5814 "$" alone or an unset sound name gets default beep (still in use). */
\r
5815 if (ms->name[1]) {
\r
5816 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5818 if (!ok) ok = MessageBeep(MB_OK);
\r
5821 /* Builtin wave resource, or "!" alone for silence */
\r
5822 if (ms->name[1]) {
\r
5823 if (ms->data == NULL) return FALSE;
\r
5824 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5830 /* .wav file. Error if not found. */
\r
5831 if (ms->data == NULL) return FALSE;
\r
5832 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5835 /* Don't print an error: this can happen innocently if the sound driver
\r
5836 is busy; for instance, if another instance of WinBoard is playing
\r
5837 a sound at about the same time. */
\r
5843 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5846 OPENFILENAME *ofn;
\r
5847 static UINT *number; /* gross that this is static */
\r
5849 switch (message) {
\r
5850 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5851 /* Center the dialog over the application window */
\r
5852 ofn = (OPENFILENAME *) lParam;
\r
5853 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5854 number = (UINT *) ofn->lCustData;
\r
5855 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5859 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5860 Translate(hDlg, 1536);
\r
5861 return FALSE; /* Allow for further processing */
\r
5864 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5865 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5867 return FALSE; /* Allow for further processing */
\r
5873 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5875 static UINT *number;
\r
5876 OPENFILENAME *ofname;
\r
5879 case WM_INITDIALOG:
\r
5880 Translate(hdlg, DLG_IndexNumber);
\r
5881 ofname = (OPENFILENAME *)lParam;
\r
5882 number = (UINT *)(ofname->lCustData);
\r
5885 ofnot = (OFNOTIFY *)lParam;
\r
5886 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5887 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5896 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5897 char *nameFilt, char *dlgTitle, UINT *number,
\r
5898 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5900 OPENFILENAME openFileName;
\r
5901 char buf1[MSG_SIZ];
\r
5904 if (fileName == NULL) fileName = buf1;
\r
5905 if (defName == NULL) {
\r
5906 safeStrCpy(fileName, "*.", 3 );
\r
5907 strcat(fileName, defExt);
\r
5909 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5911 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5912 if (number) *number = 0;
\r
5914 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5915 openFileName.hwndOwner = hwnd;
\r
5916 openFileName.hInstance = (HANDLE) hInst;
\r
5917 openFileName.lpstrFilter = nameFilt;
\r
5918 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5919 openFileName.nMaxCustFilter = 0L;
\r
5920 openFileName.nFilterIndex = 1L;
\r
5921 openFileName.lpstrFile = fileName;
\r
5922 openFileName.nMaxFile = MSG_SIZ;
\r
5923 openFileName.lpstrFileTitle = fileTitle;
\r
5924 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5925 openFileName.lpstrInitialDir = NULL;
\r
5926 openFileName.lpstrTitle = dlgTitle;
\r
5927 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5928 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5929 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5930 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5931 openFileName.nFileOffset = 0;
\r
5932 openFileName.nFileExtension = 0;
\r
5933 openFileName.lpstrDefExt = defExt;
\r
5934 openFileName.lCustData = (LONG) number;
\r
5935 openFileName.lpfnHook = oldDialog ?
\r
5936 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5937 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5939 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5940 GetOpenFileName(&openFileName)) {
\r
5941 /* open the file */
\r
5942 f = fopen(openFileName.lpstrFile, write);
\r
5944 MessageBox(hwnd, _("File open failed"), NULL,
\r
5945 MB_OK|MB_ICONEXCLAMATION);
\r
5949 int err = CommDlgExtendedError();
\r
5950 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5959 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5961 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5964 * Get the first pop-up menu in the menu template. This is the
\r
5965 * menu that TrackPopupMenu displays.
\r
5967 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5968 TranslateOneMenu(10, hmenuTrackPopup);
\r
5970 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5973 * TrackPopup uses screen coordinates, so convert the
\r
5974 * coordinates of the mouse click to screen coordinates.
\r
5976 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5978 /* Draw and track the floating pop-up menu. */
\r
5979 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5980 pt.x, pt.y, 0, hwnd, NULL);
\r
5982 /* Destroy the menu.*/
\r
5983 DestroyMenu(hmenu);
\r
5988 int sizeX, sizeY, newSizeX, newSizeY;
\r
5990 } ResizeEditPlusButtonsClosure;
\r
5993 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5995 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5999 if (hChild == cl->hText) return TRUE;
\r
6000 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
6001 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
6002 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
6003 ScreenToClient(cl->hDlg, &pt);
\r
6004 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
6005 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
6009 /* Resize a dialog that has a (rich) edit field filling most of
\r
6010 the top, with a row of buttons below */
\r
6012 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
6015 int newTextHeight, newTextWidth;
\r
6016 ResizeEditPlusButtonsClosure cl;
\r
6018 /*if (IsIconic(hDlg)) return;*/
\r
6019 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
6021 cl.hdwp = BeginDeferWindowPos(8);
\r
6023 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
6024 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6025 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6026 if (newTextHeight < 0) {
\r
6027 newSizeY += -newTextHeight;
\r
6028 newTextHeight = 0;
\r
6030 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
6031 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6037 cl.newSizeX = newSizeX;
\r
6038 cl.newSizeY = newSizeY;
\r
6039 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
6041 EndDeferWindowPos(cl.hdwp);
\r
6044 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
6046 RECT rChild, rParent;
\r
6047 int wChild, hChild, wParent, hParent;
\r
6048 int wScreen, hScreen, xNew, yNew;
\r
6051 /* Get the Height and Width of the child window */
\r
6052 GetWindowRect (hwndChild, &rChild);
\r
6053 wChild = rChild.right - rChild.left;
\r
6054 hChild = rChild.bottom - rChild.top;
\r
6056 /* Get the Height and Width of the parent window */
\r
6057 GetWindowRect (hwndParent, &rParent);
\r
6058 wParent = rParent.right - rParent.left;
\r
6059 hParent = rParent.bottom - rParent.top;
\r
6061 /* Get the display limits */
\r
6062 hdc = GetDC (hwndChild);
\r
6063 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
6064 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
6065 ReleaseDC(hwndChild, hdc);
\r
6067 /* Calculate new X position, then adjust for screen */
\r
6068 xNew = rParent.left + ((wParent - wChild) /2);
\r
6071 } else if ((xNew+wChild) > wScreen) {
\r
6072 xNew = wScreen - wChild;
\r
6075 /* Calculate new Y position, then adjust for screen */
\r
6077 yNew = rParent.top + ((hParent - hChild) /2);
\r
6080 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
6085 } else if ((yNew+hChild) > hScreen) {
\r
6086 yNew = hScreen - hChild;
\r
6089 /* Set it, and return */
\r
6090 return SetWindowPos (hwndChild, NULL,
\r
6091 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
6094 /* Center one window over another */
\r
6095 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
6097 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
6100 /*---------------------------------------------------------------------------*\
\r
6102 * Startup Dialog functions
\r
6104 \*---------------------------------------------------------------------------*/
\r
6106 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
6108 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6110 while (*cd != NULL) {
\r
6111 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
6117 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
6119 char buf1[MAX_ARG_LEN];
\r
6122 if (str[0] == '@') {
\r
6123 FILE* f = fopen(str + 1, "r");
\r
6125 DisplayFatalError(str + 1, errno, 2);
\r
6128 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
6130 buf1[len] = NULLCHAR;
\r
6134 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6137 char buf[MSG_SIZ];
\r
6138 char *end = strchr(str, '\n');
\r
6139 if (end == NULL) return;
\r
6140 memcpy(buf, str, end - str);
\r
6141 buf[end - str] = NULLCHAR;
\r
6142 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6148 SetStartupDialogEnables(HWND hDlg)
\r
6150 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6151 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6152 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6153 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6154 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6155 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6156 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6157 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6158 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6159 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6160 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6161 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6162 IsDlgButtonChecked(hDlg, OPT_View));
\r
6166 QuoteForFilename(char *filename)
\r
6168 int dquote, space;
\r
6169 dquote = strchr(filename, '"') != NULL;
\r
6170 space = strchr(filename, ' ') != NULL;
\r
6171 if (dquote || space) {
\r
6183 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6185 char buf[MSG_SIZ];
\r
6188 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6189 q = QuoteForFilename(nthcp);
\r
6190 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6191 if (*nthdir != NULLCHAR) {
\r
6192 q = QuoteForFilename(nthdir);
\r
6193 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6195 if (*nthcp == NULLCHAR) {
\r
6196 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6197 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6198 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6199 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6204 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6206 char buf[MSG_SIZ];
\r
6210 switch (message) {
\r
6211 case WM_INITDIALOG:
\r
6212 /* Center the dialog */
\r
6213 CenterWindow (hDlg, GetDesktopWindow());
\r
6214 Translate(hDlg, DLG_Startup);
\r
6215 /* Initialize the dialog items */
\r
6216 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6217 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6218 firstChessProgramNames);
\r
6219 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6220 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6221 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6222 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6223 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6224 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6225 if (*appData.icsHelper != NULLCHAR) {
\r
6226 char *q = QuoteForFilename(appData.icsHelper);
\r
6227 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6229 if (*appData.icsHost == NULLCHAR) {
\r
6230 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6231 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6232 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6233 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6234 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6237 if (appData.icsActive) {
\r
6238 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6240 else if (appData.noChessProgram) {
\r
6241 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6244 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6247 SetStartupDialogEnables(hDlg);
\r
6251 switch (LOWORD(wParam)) {
\r
6253 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6254 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6255 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6257 comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox
\r
6258 ParseArgs(StringGet, &p);
\r
6259 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6260 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6262 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6263 ParseArgs(StringGet, &p);
\r
6264 SwapEngines(singleList); // ... and then make it 'second'
\r
6265 appData.noChessProgram = FALSE;
\r
6266 appData.icsActive = FALSE;
\r
6267 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6268 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6269 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6271 ParseArgs(StringGet, &p);
\r
6272 if (appData.zippyPlay) {
\r
6273 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6274 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6276 ParseArgs(StringGet, &p);
\r
6278 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6279 appData.noChessProgram = TRUE;
\r
6280 appData.icsActive = FALSE;
\r
6282 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6283 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6286 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6287 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6289 ParseArgs(StringGet, &p);
\r
6291 EndDialog(hDlg, TRUE);
\r
6298 case IDM_HELPCONTENTS:
\r
6299 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6300 MessageBox (GetFocus(),
\r
6301 _("Unable to activate help"),
\r
6302 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6307 SetStartupDialogEnables(hDlg);
\r
6315 /*---------------------------------------------------------------------------*\
\r
6317 * About box dialog functions
\r
6319 \*---------------------------------------------------------------------------*/
\r
6321 /* Process messages for "About" dialog box */
\r
6323 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6325 switch (message) {
\r
6326 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6327 /* Center the dialog over the application window */
\r
6328 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6329 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6330 Translate(hDlg, ABOUTBOX);
\r
6334 case WM_COMMAND: /* message: received a command */
\r
6335 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6336 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6337 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6345 /*---------------------------------------------------------------------------*\
\r
6347 * Comment Dialog functions
\r
6349 \*---------------------------------------------------------------------------*/
\r
6352 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6354 static HANDLE hwndText = NULL;
\r
6355 int len, newSizeX, newSizeY, flags;
\r
6356 static int sizeX, sizeY;
\r
6361 switch (message) {
\r
6362 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6363 /* Initialize the dialog items */
\r
6364 Translate(hDlg, DLG_EditComment);
\r
6365 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6366 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6367 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6368 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6369 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6370 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6371 SetWindowText(hDlg, commentTitle);
\r
6372 if (editComment) {
\r
6373 SetFocus(hwndText);
\r
6375 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6377 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6378 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6379 MAKELPARAM(FALSE, 0));
\r
6380 /* Size and position the dialog */
\r
6381 if (!commentDialog) {
\r
6382 commentDialog = hDlg;
\r
6383 flags = SWP_NOZORDER;
\r
6384 GetClientRect(hDlg, &rect);
\r
6385 sizeX = rect.right;
\r
6386 sizeY = rect.bottom;
\r
6387 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6388 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6389 WINDOWPLACEMENT wp;
\r
6390 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6391 wp.length = sizeof(WINDOWPLACEMENT);
\r
6393 wp.showCmd = SW_SHOW;
\r
6394 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6395 wp.rcNormalPosition.left = wpComment.x;
\r
6396 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6397 wp.rcNormalPosition.top = wpComment.y;
\r
6398 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6399 SetWindowPlacement(hDlg, &wp);
\r
6401 GetClientRect(hDlg, &rect);
\r
6402 newSizeX = rect.right;
\r
6403 newSizeY = rect.bottom;
\r
6404 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6405 newSizeX, newSizeY);
\r
6410 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6413 case WM_COMMAND: /* message: received a command */
\r
6414 switch (LOWORD(wParam)) {
\r
6416 if (editComment) {
\r
6418 /* Read changed options from the dialog box */
\r
6419 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6420 len = GetWindowTextLength(hwndText);
\r
6421 str = (char *) malloc(len + 1);
\r
6422 GetWindowText(hwndText, str, len + 1);
\r
6431 ReplaceComment(commentIndex, str);
\r
6438 case OPT_CancelComment:
\r
6442 case OPT_ClearComment:
\r
6443 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6446 case OPT_EditComment:
\r
6447 EditCommentEvent();
\r
6455 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6456 if( wParam == OPT_CommentText ) {
\r
6457 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6459 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6460 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6464 pt.x = LOWORD( lpMF->lParam );
\r
6465 pt.y = HIWORD( lpMF->lParam );
\r
6467 if(lpMF->msg == WM_CHAR) {
\r
6469 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6470 index = sel.cpMin;
\r
6472 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6474 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6475 len = GetWindowTextLength(hwndText);
\r
6476 str = (char *) malloc(len + 1);
\r
6477 GetWindowText(hwndText, str, len + 1);
\r
6478 ReplaceComment(commentIndex, str);
\r
6479 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6480 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6483 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6484 lpMF->msg = WM_USER;
\r
6492 newSizeX = LOWORD(lParam);
\r
6493 newSizeY = HIWORD(lParam);
\r
6494 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6499 case WM_GETMINMAXINFO:
\r
6500 /* Prevent resizing window too small */
\r
6501 mmi = (MINMAXINFO *) lParam;
\r
6502 mmi->ptMinTrackSize.x = 100;
\r
6503 mmi->ptMinTrackSize.y = 100;
\r
6510 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6515 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6517 if (str == NULL) str = "";
\r
6518 p = (char *) malloc(2 * strlen(str) + 2);
\r
6521 if (*str == '\n') *q++ = '\r';
\r
6525 if (commentText != NULL) free(commentText);
\r
6527 commentIndex = index;
\r
6528 commentTitle = title;
\r
6530 editComment = edit;
\r
6532 if (commentDialog) {
\r
6533 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6534 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6536 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6537 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6538 hwndMain, (DLGPROC)lpProc);
\r
6539 FreeProcInstance(lpProc);
\r
6545 /*---------------------------------------------------------------------------*\
\r
6547 * Type-in move dialog functions
\r
6549 \*---------------------------------------------------------------------------*/
\r
6552 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6554 char move[MSG_SIZ];
\r
6557 switch (message) {
\r
6558 case WM_INITDIALOG:
\r
6559 move[0] = (char) lParam;
\r
6560 move[1] = NULLCHAR;
\r
6561 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6562 Translate(hDlg, DLG_TypeInMove);
\r
6563 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6564 SetWindowText(hInput, move);
\r
6566 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6570 switch (LOWORD(wParam)) {
\r
6573 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6574 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6575 TypeInDoneEvent(move);
\r
6576 EndDialog(hDlg, TRUE);
\r
6579 EndDialog(hDlg, FALSE);
\r
6590 PopUpMoveDialog(char firstchar)
\r
6594 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6595 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6596 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6597 FreeProcInstance(lpProc);
\r
6600 /*---------------------------------------------------------------------------*\
\r
6602 * Type-in name dialog functions
\r
6604 \*---------------------------------------------------------------------------*/
\r
6607 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6609 char move[MSG_SIZ];
\r
6612 switch (message) {
\r
6613 case WM_INITDIALOG:
\r
6614 move[0] = (char) lParam;
\r
6615 move[1] = NULLCHAR;
\r
6616 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6617 Translate(hDlg, DLG_TypeInName);
\r
6618 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6619 SetWindowText(hInput, move);
\r
6621 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6625 switch (LOWORD(wParam)) {
\r
6627 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6628 appData.userName = strdup(move);
\r
6631 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6632 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6633 DisplayTitle(move);
\r
6637 EndDialog(hDlg, TRUE);
\r
6640 EndDialog(hDlg, FALSE);
\r
6651 PopUpNameDialog(char firstchar)
\r
6655 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6656 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6657 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6658 FreeProcInstance(lpProc);
\r
6661 /*---------------------------------------------------------------------------*\
\r
6665 \*---------------------------------------------------------------------------*/
\r
6667 /* Nonmodal error box */
\r
6668 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6669 WPARAM wParam, LPARAM lParam);
\r
6672 ErrorPopUp(char *title, char *content)
\r
6676 BOOLEAN modal = hwndMain == NULL;
\r
6694 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6695 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6698 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6700 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6701 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6702 hwndMain, (DLGPROC)lpProc);
\r
6703 FreeProcInstance(lpProc);
\r
6710 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6711 if (errorDialog == NULL) return;
\r
6712 DestroyWindow(errorDialog);
\r
6713 errorDialog = NULL;
\r
6714 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6718 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6723 switch (message) {
\r
6724 case WM_INITDIALOG:
\r
6725 GetWindowRect(hDlg, &rChild);
\r
6728 SetWindowPos(hDlg, NULL, rChild.left,
\r
6729 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6730 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6734 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6735 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6736 and it doesn't work when you resize the dialog.
\r
6737 For now, just give it a default position.
\r
6739 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6740 Translate(hDlg, DLG_Error);
\r
6742 errorDialog = hDlg;
\r
6743 SetWindowText(hDlg, errorTitle);
\r
6744 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6745 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6749 switch (LOWORD(wParam)) {
\r
6752 if (errorDialog == hDlg) errorDialog = NULL;
\r
6753 DestroyWindow(hDlg);
\r
6765 HWND gothicDialog = NULL;
\r
6768 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6772 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6774 switch (message) {
\r
6775 case WM_INITDIALOG:
\r
6776 GetWindowRect(hDlg, &rChild);
\r
6778 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6782 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6783 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6784 and it doesn't work when you resize the dialog.
\r
6785 For now, just give it a default position.
\r
6787 gothicDialog = hDlg;
\r
6788 SetWindowText(hDlg, errorTitle);
\r
6789 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6790 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6794 switch (LOWORD(wParam)) {
\r
6797 if (errorDialog == hDlg) errorDialog = NULL;
\r
6798 DestroyWindow(hDlg);
\r
6810 GothicPopUp(char *title, VariantClass variant)
\r
6813 static char *lastTitle;
\r
6815 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6816 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6818 if(lastTitle != title && gothicDialog != NULL) {
\r
6819 DestroyWindow(gothicDialog);
\r
6820 gothicDialog = NULL;
\r
6822 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6823 title = lastTitle;
\r
6824 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6825 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6826 hwndMain, (DLGPROC)lpProc);
\r
6827 FreeProcInstance(lpProc);
\r
6832 /*---------------------------------------------------------------------------*\
\r
6834 * Ics Interaction console functions
\r
6836 \*---------------------------------------------------------------------------*/
\r
6838 #define HISTORY_SIZE 64
\r
6839 static char *history[HISTORY_SIZE];
\r
6840 int histIn = 0, histP = 0;
\r
6843 SaveInHistory(char *cmd)
\r
6845 if (history[histIn] != NULL) {
\r
6846 free(history[histIn]);
\r
6847 history[histIn] = NULL;
\r
6849 if (*cmd == NULLCHAR) return;
\r
6850 history[histIn] = StrSave(cmd);
\r
6851 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6852 if (history[histIn] != NULL) {
\r
6853 free(history[histIn]);
\r
6854 history[histIn] = NULL;
\r
6860 PrevInHistory(char *cmd)
\r
6863 if (histP == histIn) {
\r
6864 if (history[histIn] != NULL) free(history[histIn]);
\r
6865 history[histIn] = StrSave(cmd);
\r
6867 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6868 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6870 return history[histP];
\r
6876 if (histP == histIn) return NULL;
\r
6877 histP = (histP + 1) % HISTORY_SIZE;
\r
6878 return history[histP];
\r
6882 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6886 hmenu = LoadMenu(hInst, "TextMenu");
\r
6887 h = GetSubMenu(hmenu, 0);
\r
6889 if (strcmp(e->item, "-") == 0) {
\r
6890 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6891 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6892 int flags = MF_STRING, j = 0;
\r
6893 if (e->item[0] == '|') {
\r
6894 flags |= MF_MENUBARBREAK;
\r
6897 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6898 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6906 WNDPROC consoleTextWindowProc;
\r
6909 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6911 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6912 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6916 SetWindowText(hInput, command);
\r
6918 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6920 sel.cpMin = 999999;
\r
6921 sel.cpMax = 999999;
\r
6922 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6927 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6928 if (sel.cpMin == sel.cpMax) {
\r
6929 /* Expand to surrounding word */
\r
6932 tr.chrg.cpMax = sel.cpMin;
\r
6933 tr.chrg.cpMin = --sel.cpMin;
\r
6934 if (sel.cpMin < 0) break;
\r
6935 tr.lpstrText = name;
\r
6936 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6937 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6941 tr.chrg.cpMin = sel.cpMax;
\r
6942 tr.chrg.cpMax = ++sel.cpMax;
\r
6943 tr.lpstrText = name;
\r
6944 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6945 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6948 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6949 MessageBeep(MB_ICONEXCLAMATION);
\r
6953 tr.lpstrText = name;
\r
6954 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6956 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6957 MessageBeep(MB_ICONEXCLAMATION);
\r
6960 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6963 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
6964 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6965 SetWindowText(hInput, buf);
\r
6966 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6968 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6969 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6970 SetWindowText(hInput, buf);
\r
6971 sel.cpMin = 999999;
\r
6972 sel.cpMax = 999999;
\r
6973 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6979 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6984 switch (message) {
\r
6986 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6987 if(wParam=='R') return 0;
\r
6990 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6993 sel.cpMin = 999999;
\r
6994 sel.cpMax = 999999;
\r
6995 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6996 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
7001 if(wParam != '\022') {
\r
7002 if (wParam == '\t') {
\r
7003 if (GetKeyState(VK_SHIFT) < 0) {
\r
7005 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7006 if (buttonDesc[0].hwnd) {
\r
7007 SetFocus(buttonDesc[0].hwnd);
\r
7009 SetFocus(hwndMain);
\r
7013 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
7016 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7017 JAWS_DELETE( SetFocus(hInput); )
\r
7018 SendMessage(hInput, message, wParam, lParam);
\r
7021 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
7023 case WM_RBUTTONDOWN:
\r
7024 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
7025 /* Move selection here if it was empty */
\r
7027 pt.x = LOWORD(lParam);
\r
7028 pt.y = HIWORD(lParam);
\r
7029 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7030 if (sel.cpMin == sel.cpMax) {
\r
7031 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
7032 sel.cpMax = sel.cpMin;
\r
7033 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7035 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
7036 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
7038 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
7039 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7040 if (sel.cpMin == sel.cpMax) {
\r
7041 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7042 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
7044 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7045 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7047 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
7048 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
7049 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
7050 MenuPopup(hwnd, pt, hmenu, -1);
\r
7054 case WM_RBUTTONUP:
\r
7055 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7056 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7057 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7061 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7063 return SendMessage(hInput, message, wParam, lParam);
\r
7064 case WM_MBUTTONDOWN:
\r
7065 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7067 switch (LOWORD(wParam)) {
\r
7068 case IDM_QuickPaste:
\r
7070 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7071 if (sel.cpMin == sel.cpMax) {
\r
7072 MessageBeep(MB_ICONEXCLAMATION);
\r
7075 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7076 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7077 SendMessage(hInput, WM_PASTE, 0, 0);
\r
7082 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7085 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7088 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7092 int i = LOWORD(wParam) - IDM_CommandX;
\r
7093 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
7094 icsTextMenuEntry[i].command != NULL) {
\r
7095 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
7096 icsTextMenuEntry[i].getname,
\r
7097 icsTextMenuEntry[i].immediate);
\r
7105 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
7108 WNDPROC consoleInputWindowProc;
\r
7111 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7113 char buf[MSG_SIZ];
\r
7115 static BOOL sendNextChar = FALSE;
\r
7116 static BOOL quoteNextChar = FALSE;
\r
7117 InputSource *is = consoleInputSource;
\r
7121 switch (message) {
\r
7123 if (!appData.localLineEditing || sendNextChar) {
\r
7124 is->buf[0] = (CHAR) wParam;
\r
7126 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7127 sendNextChar = FALSE;
\r
7130 if (quoteNextChar) {
\r
7131 buf[0] = (char) wParam;
\r
7132 buf[1] = NULLCHAR;
\r
7133 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7134 quoteNextChar = FALSE;
\r
7138 case '\r': /* Enter key */
\r
7139 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7140 if (consoleEcho) SaveInHistory(is->buf);
\r
7141 is->buf[is->count++] = '\n';
\r
7142 is->buf[is->count] = NULLCHAR;
\r
7143 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7144 if (consoleEcho) {
\r
7145 ConsoleOutput(is->buf, is->count, TRUE);
\r
7146 } else if (appData.localLineEditing) {
\r
7147 ConsoleOutput("\n", 1, TRUE);
\r
7150 case '\033': /* Escape key */
\r
7151 SetWindowText(hwnd, "");
\r
7152 cf.cbSize = sizeof(CHARFORMAT);
\r
7153 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7154 if (consoleEcho) {
\r
7155 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7157 cf.crTextColor = COLOR_ECHOOFF;
\r
7159 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7160 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7162 case '\t': /* Tab key */
\r
7163 if (GetKeyState(VK_SHIFT) < 0) {
\r
7165 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7168 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7169 if (buttonDesc[0].hwnd) {
\r
7170 SetFocus(buttonDesc[0].hwnd);
\r
7172 SetFocus(hwndMain);
\r
7176 case '\023': /* Ctrl+S */
\r
7177 sendNextChar = TRUE;
\r
7179 case '\021': /* Ctrl+Q */
\r
7180 quoteNextChar = TRUE;
\r
7190 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7191 p = PrevInHistory(buf);
\r
7193 SetWindowText(hwnd, p);
\r
7194 sel.cpMin = 999999;
\r
7195 sel.cpMax = 999999;
\r
7196 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7201 p = NextInHistory();
\r
7203 SetWindowText(hwnd, p);
\r
7204 sel.cpMin = 999999;
\r
7205 sel.cpMax = 999999;
\r
7206 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7212 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7216 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7220 case WM_MBUTTONDOWN:
\r
7221 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7222 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7224 case WM_RBUTTONUP:
\r
7225 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7226 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7227 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7231 hmenu = LoadMenu(hInst, "InputMenu");
\r
7232 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7233 if (sel.cpMin == sel.cpMax) {
\r
7234 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7235 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7237 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7238 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7240 pt.x = LOWORD(lParam);
\r
7241 pt.y = HIWORD(lParam);
\r
7242 MenuPopup(hwnd, pt, hmenu, -1);
\r
7246 switch (LOWORD(wParam)) {
\r
7248 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7250 case IDM_SelectAll:
\r
7252 sel.cpMax = -1; /*999999?*/
\r
7253 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7256 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7259 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7262 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7267 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7270 #define CO_MAX 100000
\r
7271 #define CO_TRIM 1000
\r
7274 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7276 static SnapData sd;
\r
7277 HWND hText, hInput;
\r
7279 static int sizeX, sizeY;
\r
7280 int newSizeX, newSizeY;
\r
7284 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7285 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7287 switch (message) {
\r
7289 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7291 ENLINK *pLink = (ENLINK*)lParam;
\r
7292 if (pLink->msg == WM_LBUTTONUP)
\r
7296 tr.chrg = pLink->chrg;
\r
7297 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7298 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7299 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7300 free(tr.lpstrText);
\r
7304 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7305 hwndConsole = hDlg;
\r
7307 consoleTextWindowProc = (WNDPROC)
\r
7308 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7309 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7310 consoleInputWindowProc = (WNDPROC)
\r
7311 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7312 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7313 Colorize(ColorNormal, TRUE);
\r
7314 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7315 ChangedConsoleFont();
\r
7316 GetClientRect(hDlg, &rect);
\r
7317 sizeX = rect.right;
\r
7318 sizeY = rect.bottom;
\r
7319 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7320 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7321 WINDOWPLACEMENT wp;
\r
7322 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7323 wp.length = sizeof(WINDOWPLACEMENT);
\r
7325 wp.showCmd = SW_SHOW;
\r
7326 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7327 wp.rcNormalPosition.left = wpConsole.x;
\r
7328 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7329 wp.rcNormalPosition.top = wpConsole.y;
\r
7330 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7331 SetWindowPlacement(hDlg, &wp);
\r
7334 // [HGM] Chessknight's change 2004-07-13
\r
7335 else { /* Determine Defaults */
\r
7336 WINDOWPLACEMENT wp;
\r
7337 wpConsole.x = wpMain.width + 1;
\r
7338 wpConsole.y = wpMain.y;
\r
7339 wpConsole.width = screenWidth - wpMain.width;
\r
7340 wpConsole.height = wpMain.height;
\r
7341 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7342 wp.length = sizeof(WINDOWPLACEMENT);
\r
7344 wp.showCmd = SW_SHOW;
\r
7345 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7346 wp.rcNormalPosition.left = wpConsole.x;
\r
7347 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7348 wp.rcNormalPosition.top = wpConsole.y;
\r
7349 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7350 SetWindowPlacement(hDlg, &wp);
\r
7353 // Allow hText to highlight URLs and send notifications on them
\r
7354 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7355 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7356 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7357 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7371 if (IsIconic(hDlg)) break;
\r
7372 newSizeX = LOWORD(lParam);
\r
7373 newSizeY = HIWORD(lParam);
\r
7374 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7375 RECT rectText, rectInput;
\r
7377 int newTextHeight, newTextWidth;
\r
7378 GetWindowRect(hText, &rectText);
\r
7379 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7380 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7381 if (newTextHeight < 0) {
\r
7382 newSizeY += -newTextHeight;
\r
7383 newTextHeight = 0;
\r
7385 SetWindowPos(hText, NULL, 0, 0,
\r
7386 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7387 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7388 pt.x = rectInput.left;
\r
7389 pt.y = rectInput.top + newSizeY - sizeY;
\r
7390 ScreenToClient(hDlg, &pt);
\r
7391 SetWindowPos(hInput, NULL,
\r
7392 pt.x, pt.y, /* needs client coords */
\r
7393 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7394 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7400 case WM_GETMINMAXINFO:
\r
7401 /* Prevent resizing window too small */
\r
7402 mmi = (MINMAXINFO *) lParam;
\r
7403 mmi->ptMinTrackSize.x = 100;
\r
7404 mmi->ptMinTrackSize.y = 100;
\r
7407 /* [AS] Snapping */
\r
7408 case WM_ENTERSIZEMOVE:
\r
7409 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7412 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7415 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7417 case WM_EXITSIZEMOVE:
\r
7418 UpdateICSWidth(hText);
\r
7419 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7422 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7430 if (hwndConsole) return;
\r
7431 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7432 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7437 ConsoleOutput(char* data, int length, int forceVisible)
\r
7442 char buf[CO_MAX+1];
\r
7445 static int delayLF = 0;
\r
7446 CHARRANGE savesel, sel;
\r
7448 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7456 while (length--) {
\r
7464 } else if (*p == '\007') {
\r
7465 MyPlaySound(&sounds[(int)SoundBell]);
\r
7472 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7473 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7474 /* Save current selection */
\r
7475 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7476 exlen = GetWindowTextLength(hText);
\r
7477 /* Find out whether current end of text is visible */
\r
7478 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7479 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7480 /* Trim existing text if it's too long */
\r
7481 if (exlen + (q - buf) > CO_MAX) {
\r
7482 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7485 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7486 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7488 savesel.cpMin -= trim;
\r
7489 savesel.cpMax -= trim;
\r
7490 if (exlen < 0) exlen = 0;
\r
7491 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7492 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7494 /* Append the new text */
\r
7495 sel.cpMin = exlen;
\r
7496 sel.cpMax = exlen;
\r
7497 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7498 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7499 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7500 if (forceVisible || exlen == 0 ||
\r
7501 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7502 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7503 /* Scroll to make new end of text visible if old end of text
\r
7504 was visible or new text is an echo of user typein */
\r
7505 sel.cpMin = 9999999;
\r
7506 sel.cpMax = 9999999;
\r
7507 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7508 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7509 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7510 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7512 if (savesel.cpMax == exlen || forceVisible) {
\r
7513 /* Move insert point to new end of text if it was at the old
\r
7514 end of text or if the new text is an echo of user typein */
\r
7515 sel.cpMin = 9999999;
\r
7516 sel.cpMax = 9999999;
\r
7517 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7519 /* Restore previous selection */
\r
7520 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7522 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7529 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7533 COLORREF oldFg, oldBg;
\r
7537 if(copyNumber > 1)
\r
7538 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7540 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7541 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7542 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7545 rect.right = x + squareSize;
\r
7547 rect.bottom = y + squareSize;
\r
7550 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7551 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7552 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7553 &rect, str, strlen(str), NULL);
\r
7555 (void) SetTextColor(hdc, oldFg);
\r
7556 (void) SetBkColor(hdc, oldBg);
\r
7557 (void) SelectObject(hdc, oldFont);
\r
7561 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7562 RECT *rect, char *color, char *flagFell)
\r
7566 COLORREF oldFg, oldBg;
\r
7569 if (twoBoards && partnerUp) return;
\r
7570 if (appData.clockMode) {
\r
7572 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7574 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7581 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7582 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7584 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7585 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7587 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7591 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7592 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7593 rect, str, strlen(str), NULL);
\r
7594 if(logoHeight > 0 && appData.clockMode) {
\r
7596 str += strlen(color)+2;
\r
7597 r.top = rect->top + logoHeight/2;
\r
7598 r.left = rect->left;
\r
7599 r.right = rect->right;
\r
7600 r.bottom = rect->bottom;
\r
7601 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7602 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7603 &r, str, strlen(str), NULL);
\r
7605 (void) SetTextColor(hdc, oldFg);
\r
7606 (void) SetBkColor(hdc, oldBg);
\r
7607 (void) SelectObject(hdc, oldFont);
\r
7612 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7618 if( count <= 0 ) {
\r
7619 if (appData.debugMode) {
\r
7620 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7623 return ERROR_INVALID_USER_BUFFER;
\r
7626 ResetEvent(ovl->hEvent);
\r
7627 ovl->Offset = ovl->OffsetHigh = 0;
\r
7628 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7632 err = GetLastError();
\r
7633 if (err == ERROR_IO_PENDING) {
\r
7634 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7638 err = GetLastError();
\r
7645 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7650 ResetEvent(ovl->hEvent);
\r
7651 ovl->Offset = ovl->OffsetHigh = 0;
\r
7652 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7656 err = GetLastError();
\r
7657 if (err == ERROR_IO_PENDING) {
\r
7658 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7662 err = GetLastError();
\r
7668 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7669 void CheckForInputBufferFull( InputSource * is )
\r
7671 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7672 /* Look for end of line */
\r
7673 char * p = is->buf;
\r
7675 while( p < is->next && *p != '\n' ) {
\r
7679 if( p >= is->next ) {
\r
7680 if (appData.debugMode) {
\r
7681 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7684 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7685 is->count = (DWORD) -1;
\r
7686 is->next = is->buf;
\r
7692 InputThread(LPVOID arg)
\r
7697 is = (InputSource *) arg;
\r
7698 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7699 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7700 while (is->hThread != NULL) {
\r
7701 is->error = DoReadFile(is->hFile, is->next,
\r
7702 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7703 &is->count, &ovl);
\r
7704 if (is->error == NO_ERROR) {
\r
7705 is->next += is->count;
\r
7707 if (is->error == ERROR_BROKEN_PIPE) {
\r
7708 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7711 is->count = (DWORD) -1;
\r
7712 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7717 CheckForInputBufferFull( is );
\r
7719 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7721 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7723 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7726 CloseHandle(ovl.hEvent);
\r
7727 CloseHandle(is->hFile);
\r
7729 if (appData.debugMode) {
\r
7730 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7737 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7739 NonOvlInputThread(LPVOID arg)
\r
7746 is = (InputSource *) arg;
\r
7747 while (is->hThread != NULL) {
\r
7748 is->error = ReadFile(is->hFile, is->next,
\r
7749 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7750 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7751 if (is->error == NO_ERROR) {
\r
7752 /* Change CRLF to LF */
\r
7753 if (is->next > is->buf) {
\r
7755 i = is->count + 1;
\r
7763 if (prev == '\r' && *p == '\n') {
\r
7775 if (is->error == ERROR_BROKEN_PIPE) {
\r
7776 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7779 is->count = (DWORD) -1;
\r
7783 CheckForInputBufferFull( is );
\r
7785 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7787 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7789 if (is->count < 0) break; /* Quit on error */
\r
7791 CloseHandle(is->hFile);
\r
7796 SocketInputThread(LPVOID arg)
\r
7800 is = (InputSource *) arg;
\r
7801 while (is->hThread != NULL) {
\r
7802 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7803 if ((int)is->count == SOCKET_ERROR) {
\r
7804 is->count = (DWORD) -1;
\r
7805 is->error = WSAGetLastError();
\r
7807 is->error = NO_ERROR;
\r
7808 is->next += is->count;
\r
7809 if (is->count == 0 && is->second == is) {
\r
7810 /* End of file on stderr; quit with no message */
\r
7814 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7816 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7818 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7824 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7828 is = (InputSource *) lParam;
\r
7829 if (is->lineByLine) {
\r
7830 /* Feed in lines one by one */
\r
7831 char *p = is->buf;
\r
7833 while (q < is->next) {
\r
7834 if (*q++ == '\n') {
\r
7835 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7840 /* Move any partial line to the start of the buffer */
\r
7842 while (p < is->next) {
\r
7847 if (is->error != NO_ERROR || is->count == 0) {
\r
7848 /* Notify backend of the error. Note: If there was a partial
\r
7849 line at the end, it is not flushed through. */
\r
7850 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7853 /* Feed in the whole chunk of input at once */
\r
7854 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7855 is->next = is->buf;
\r
7859 /*---------------------------------------------------------------------------*\
\r
7861 * Menu enables. Used when setting various modes.
\r
7863 \*---------------------------------------------------------------------------*/
\r
7871 GreyRevert(Boolean grey)
\r
7872 { // [HGM] vari: for retracting variations in local mode
\r
7873 HMENU hmenu = GetMenu(hwndMain);
\r
7874 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7875 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7879 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7881 while (enab->item > 0) {
\r
7882 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7887 Enables gnuEnables[] = {
\r
7888 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7889 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7890 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7891 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7892 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7893 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7894 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7895 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7896 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7897 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7898 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7899 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7900 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7902 // Needed to switch from ncp to GNU mode on Engine Load
\r
7903 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7904 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7905 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7906 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7907 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7908 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7909 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
7910 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7911 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
7912 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
7913 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7914 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7915 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7916 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7920 Enables icsEnables[] = {
\r
7921 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7922 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7923 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7924 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7925 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7926 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7927 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7928 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7929 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7930 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7931 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7932 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7933 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7934 { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },
\r
7935 { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },
\r
7936 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7937 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7938 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7939 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7940 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
7945 Enables zippyEnables[] = {
\r
7946 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7947 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7948 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7949 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7954 Enables ncpEnables[] = {
\r
7955 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7956 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7957 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7958 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7959 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7960 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7961 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7962 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7963 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7964 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7965 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7966 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7967 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7968 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7969 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7970 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7971 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7972 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7973 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7974 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7975 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7976 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
7980 Enables trainingOnEnables[] = {
\r
7981 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7982 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
7983 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7984 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7985 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7986 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7987 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7988 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7989 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7993 Enables trainingOffEnables[] = {
\r
7994 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7995 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
7996 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7997 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7998 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7999 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
8000 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
8001 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8002 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
8006 /* These modify either ncpEnables or gnuEnables */
\r
8007 Enables cmailEnables[] = {
\r
8008 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
8009 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
8010 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
8011 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
8012 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
8013 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
8014 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
8018 Enables machineThinkingEnables[] = {
\r
8019 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8020 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
8021 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
8022 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8023 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
8024 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8025 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8026 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8027 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8028 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
8029 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8030 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8031 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8032 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8033 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
8034 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
8038 Enables userThinkingEnables[] = {
\r
8039 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8040 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
8041 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
8042 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8043 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
8044 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8045 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8046 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8047 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8048 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
8049 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
8050 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
8051 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
8052 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
8053 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
8054 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
8058 /*---------------------------------------------------------------------------*\
\r
8060 * Front-end interface functions exported by XBoard.
\r
8061 * Functions appear in same order as prototypes in frontend.h.
\r
8063 \*---------------------------------------------------------------------------*/
\r
8065 CheckMark(UINT item, int state)
\r
8067 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
8073 static UINT prevChecked = 0;
\r
8074 static int prevPausing = 0;
\r
8077 if (pausing != prevPausing) {
\r
8078 prevPausing = pausing;
\r
8079 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
8080 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
8081 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
8084 switch (gameMode) {
\r
8085 case BeginningOfGame:
\r
8086 if (appData.icsActive)
\r
8087 nowChecked = IDM_IcsClient;
\r
8088 else if (appData.noChessProgram)
\r
8089 nowChecked = IDM_EditGame;
\r
8091 nowChecked = IDM_MachineBlack;
\r
8093 case MachinePlaysBlack:
\r
8094 nowChecked = IDM_MachineBlack;
\r
8096 case MachinePlaysWhite:
\r
8097 nowChecked = IDM_MachineWhite;
\r
8099 case TwoMachinesPlay:
\r
8100 nowChecked = IDM_TwoMachines;
\r
8103 nowChecked = IDM_AnalysisMode;
\r
8106 nowChecked = IDM_AnalyzeFile;
\r
8109 nowChecked = IDM_EditGame;
\r
8111 case PlayFromGameFile:
\r
8112 nowChecked = IDM_LoadGame;
\r
8114 case EditPosition:
\r
8115 nowChecked = IDM_EditPosition;
\r
8118 nowChecked = IDM_Training;
\r
8120 case IcsPlayingWhite:
\r
8121 case IcsPlayingBlack:
\r
8122 case IcsObserving:
\r
8124 nowChecked = IDM_IcsClient;
\r
8131 CheckMark(prevChecked, MF_UNCHECKED);
\r
8132 CheckMark(nowChecked, MF_CHECKED);
\r
8133 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8135 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8136 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8137 MF_BYCOMMAND|MF_ENABLED);
\r
8139 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8140 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8143 prevChecked = nowChecked;
\r
8145 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8146 if (appData.icsActive) {
\r
8147 if (appData.icsEngineAnalyze) {
\r
8148 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8150 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8153 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8159 HMENU hmenu = GetMenu(hwndMain);
\r
8160 SetMenuEnables(hmenu, icsEnables);
\r
8161 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8162 MF_BYCOMMAND|MF_ENABLED);
\r
8164 if (appData.zippyPlay) {
\r
8165 SetMenuEnables(hmenu, zippyEnables);
\r
8166 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8167 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8168 MF_BYCOMMAND|MF_ENABLED);
\r
8176 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8182 HMENU hmenu = GetMenu(hwndMain);
\r
8183 SetMenuEnables(hmenu, ncpEnables);
\r
8184 DrawMenuBar(hwndMain);
\r
8190 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8194 SetTrainingModeOn()
\r
8197 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8198 for (i = 0; i < N_BUTTONS; i++) {
\r
8199 if (buttonDesc[i].hwnd != NULL)
\r
8200 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8205 VOID SetTrainingModeOff()
\r
8208 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8209 for (i = 0; i < N_BUTTONS; i++) {
\r
8210 if (buttonDesc[i].hwnd != NULL)
\r
8211 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8217 SetUserThinkingEnables()
\r
8219 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8223 SetMachineThinkingEnables()
\r
8225 HMENU hMenu = GetMenu(hwndMain);
\r
8226 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8228 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8230 if (gameMode == MachinePlaysBlack) {
\r
8231 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8232 } else if (gameMode == MachinePlaysWhite) {
\r
8233 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8234 } else if (gameMode == TwoMachinesPlay) {
\r
8235 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8241 DisplayTitle(char *str)
\r
8243 char title[MSG_SIZ], *host;
\r
8244 if (str[0] != NULLCHAR) {
\r
8245 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8246 } else if (appData.icsActive) {
\r
8247 if (appData.icsCommPort[0] != NULLCHAR)
\r
8250 host = appData.icsHost;
\r
8251 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8252 } else if (appData.noChessProgram) {
\r
8253 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8255 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8256 strcat(title, ": ");
\r
8257 strcat(title, first.tidy);
\r
8259 SetWindowText(hwndMain, title);
\r
8264 DisplayMessage(char *str1, char *str2)
\r
8268 int remain = MESSAGE_TEXT_MAX - 1;
\r
8271 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8272 messageText[0] = NULLCHAR;
\r
8274 len = strlen(str1);
\r
8275 if (len > remain) len = remain;
\r
8276 strncpy(messageText, str1, len);
\r
8277 messageText[len] = NULLCHAR;
\r
8280 if (*str2 && remain >= 2) {
\r
8282 strcat(messageText, " ");
\r
8285 len = strlen(str2);
\r
8286 if (len > remain) len = remain;
\r
8287 strncat(messageText, str2, len);
\r
8289 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8290 safeStrCpy(lastMsg, messageText, MSG_SIZ);
\r
8292 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8296 hdc = GetDC(hwndMain);
\r
8297 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8298 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8299 &messageRect, messageText, strlen(messageText), NULL);
\r
8300 (void) SelectObject(hdc, oldFont);
\r
8301 (void) ReleaseDC(hwndMain, hdc);
\r
8305 DisplayError(char *str, int error)
\r
8307 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8311 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8313 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8314 NULL, error, LANG_NEUTRAL,
\r
8315 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8317 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8319 ErrorMap *em = errmap;
\r
8320 while (em->err != 0 && em->err != error) em++;
\r
8321 if (em->err != 0) {
\r
8322 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8324 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8329 ErrorPopUp(_("Error"), buf);
\r
8334 DisplayMoveError(char *str)
\r
8336 fromX = fromY = -1;
\r
8337 ClearHighlights();
\r
8338 DrawPosition(FALSE, NULL);
\r
8339 if (appData.popupMoveErrors) {
\r
8340 ErrorPopUp(_("Error"), str);
\r
8342 DisplayMessage(str, "");
\r
8343 moveErrorMessageUp = TRUE;
\r
8348 DisplayFatalError(char *str, int error, int exitStatus)
\r
8350 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8352 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8355 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8356 NULL, error, LANG_NEUTRAL,
\r
8357 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8359 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8361 ErrorMap *em = errmap;
\r
8362 while (em->err != 0 && em->err != error) em++;
\r
8363 if (em->err != 0) {
\r
8364 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8366 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8371 if (appData.debugMode) {
\r
8372 fprintf(debugFP, "%s: %s\n", label, str);
\r
8374 if (appData.popupExitMessage) {
\r
8375 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8376 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8378 ExitEvent(exitStatus);
\r
8383 DisplayInformation(char *str)
\r
8385 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8390 DisplayNote(char *str)
\r
8392 ErrorPopUp(_("Note"), str);
\r
8397 char *title, *question, *replyPrefix;
\r
8402 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8404 static QuestionParams *qp;
\r
8405 char reply[MSG_SIZ];
\r
8408 switch (message) {
\r
8409 case WM_INITDIALOG:
\r
8410 qp = (QuestionParams *) lParam;
\r
8411 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8412 Translate(hDlg, DLG_Question);
\r
8413 SetWindowText(hDlg, qp->title);
\r
8414 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8415 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8419 switch (LOWORD(wParam)) {
\r
8421 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8422 if (*reply) strcat(reply, " ");
\r
8423 len = strlen(reply);
\r
8424 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8425 strcat(reply, "\n");
\r
8426 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8427 EndDialog(hDlg, TRUE);
\r
8428 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8431 EndDialog(hDlg, FALSE);
\r
8442 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8444 QuestionParams qp;
\r
8448 qp.question = question;
\r
8449 qp.replyPrefix = replyPrefix;
\r
8451 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8452 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8453 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8454 FreeProcInstance(lpProc);
\r
8457 /* [AS] Pick FRC position */
\r
8458 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8460 static int * lpIndexFRC;
\r
8466 case WM_INITDIALOG:
\r
8467 lpIndexFRC = (int *) lParam;
\r
8469 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8470 Translate(hDlg, DLG_NewGameFRC);
\r
8472 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8473 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8474 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8475 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8480 switch( LOWORD(wParam) ) {
\r
8482 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8483 EndDialog( hDlg, 0 );
\r
8484 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8487 EndDialog( hDlg, 1 );
\r
8489 case IDC_NFG_Edit:
\r
8490 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8491 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8493 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8496 case IDC_NFG_Random:
\r
8497 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8498 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8511 int index = appData.defaultFrcPosition;
\r
8512 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8514 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8516 if( result == 0 ) {
\r
8517 appData.defaultFrcPosition = index;
\r
8523 /* [AS] Game list options. Refactored by HGM */
\r
8525 HWND gameListOptionsDialog;
\r
8527 // low-level front-end: clear text edit / list widget
\r
8531 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8534 // low-level front-end: clear text edit / list widget
\r
8536 GLT_DeSelectList()
\r
8538 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8541 // low-level front-end: append line to text edit / list widget
\r
8543 GLT_AddToList( char *name )
\r
8546 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8550 // low-level front-end: get line from text edit / list widget
\r
8552 GLT_GetFromList( int index, char *name )
\r
8555 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8561 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8563 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8564 int idx2 = idx1 + delta;
\r
8565 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8567 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8570 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8571 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8572 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8573 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8577 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8581 case WM_INITDIALOG:
\r
8582 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8584 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8585 Translate(hDlg, DLG_GameListOptions);
\r
8587 /* Initialize list */
\r
8588 GLT_TagsToList( lpUserGLT );
\r
8590 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8595 switch( LOWORD(wParam) ) {
\r
8598 EndDialog( hDlg, 0 );
\r
8601 EndDialog( hDlg, 1 );
\r
8604 case IDC_GLT_Default:
\r
8605 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8608 case IDC_GLT_Restore:
\r
8609 GLT_TagsToList( appData.gameListTags );
\r
8613 GLT_MoveSelection( hDlg, -1 );
\r
8616 case IDC_GLT_Down:
\r
8617 GLT_MoveSelection( hDlg, +1 );
\r
8627 int GameListOptions()
\r
8630 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8632 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8634 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8636 if( result == 0 ) {
\r
8637 /* [AS] Memory leak here! */
\r
8638 appData.gameListTags = strdup( lpUserGLT );
\r
8645 DisplayIcsInteractionTitle(char *str)
\r
8647 char consoleTitle[MSG_SIZ];
\r
8649 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8650 SetWindowText(hwndConsole, consoleTitle);
\r
8652 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
8653 char buf[MSG_SIZ], *p = buf, *q;
\r
8654 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
8656 q = strchr(p, ';');
\r
8658 if(*p) ChatPopUp(p);
\r
8662 SetActiveWindow(hwndMain);
\r
8666 DrawPosition(int fullRedraw, Board board)
\r
8668 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8671 void NotifyFrontendLogin()
\r
8674 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8680 fromX = fromY = -1;
\r
8681 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8682 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8683 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8684 dragInfo.lastpos = dragInfo.pos;
\r
8685 dragInfo.start.x = dragInfo.start.y = -1;
\r
8686 dragInfo.from = dragInfo.start;
\r
8688 DrawPosition(TRUE, NULL);
\r
8695 CommentPopUp(char *title, char *str)
\r
8697 HWND hwnd = GetActiveWindow();
\r
8698 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8700 SetActiveWindow(hwnd);
\r
8704 CommentPopDown(void)
\r
8706 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8707 if (commentDialog) {
\r
8708 ShowWindow(commentDialog, SW_HIDE);
\r
8710 commentUp = FALSE;
\r
8714 EditCommentPopUp(int index, char *title, char *str)
\r
8716 EitherCommentPopUp(index, title, str, TRUE);
\r
8723 MyPlaySound(&sounds[(int)SoundMove]);
\r
8726 VOID PlayIcsWinSound()
\r
8728 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8731 VOID PlayIcsLossSound()
\r
8733 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8736 VOID PlayIcsDrawSound()
\r
8738 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8741 VOID PlayIcsUnfinishedSound()
\r
8743 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8749 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8755 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8763 consoleEcho = TRUE;
\r
8764 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8765 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8766 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8775 consoleEcho = FALSE;
\r
8776 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8777 /* This works OK: set text and background both to the same color */
\r
8779 cf.crTextColor = COLOR_ECHOOFF;
\r
8780 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8781 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8784 /* No Raw()...? */
\r
8786 void Colorize(ColorClass cc, int continuation)
\r
8788 currentColorClass = cc;
\r
8789 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8790 consoleCF.crTextColor = textAttribs[cc].color;
\r
8791 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8792 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8798 static char buf[MSG_SIZ];
\r
8799 DWORD bufsiz = MSG_SIZ;
\r
8801 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8802 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8804 if (!GetUserName(buf, &bufsiz)) {
\r
8805 /*DisplayError("Error getting user name", GetLastError());*/
\r
8806 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8814 static char buf[MSG_SIZ];
\r
8815 DWORD bufsiz = MSG_SIZ;
\r
8817 if (!GetComputerName(buf, &bufsiz)) {
\r
8818 /*DisplayError("Error getting host name", GetLastError());*/
\r
8819 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8826 ClockTimerRunning()
\r
8828 return clockTimerEvent != 0;
\r
8834 if (clockTimerEvent == 0) return FALSE;
\r
8835 KillTimer(hwndMain, clockTimerEvent);
\r
8836 clockTimerEvent = 0;
\r
8841 StartClockTimer(long millisec)
\r
8843 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8844 (UINT) millisec, NULL);
\r
8848 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8851 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8853 if(appData.noGUI) return;
\r
8854 hdc = GetDC(hwndMain);
\r
8855 if (!IsIconic(hwndMain)) {
\r
8856 DisplayAClock(hdc, timeRemaining, highlight,
\r
8857 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8859 if (highlight && iconCurrent == iconBlack) {
\r
8860 iconCurrent = iconWhite;
\r
8861 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8862 if (IsIconic(hwndMain)) {
\r
8863 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8866 (void) ReleaseDC(hwndMain, hdc);
\r
8868 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8872 DisplayBlackClock(long timeRemaining, int highlight)
\r
8875 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8877 if(appData.noGUI) return;
\r
8878 hdc = GetDC(hwndMain);
\r
8879 if (!IsIconic(hwndMain)) {
\r
8880 DisplayAClock(hdc, timeRemaining, highlight,
\r
8881 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8883 if (highlight && iconCurrent == iconWhite) {
\r
8884 iconCurrent = iconBlack;
\r
8885 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8886 if (IsIconic(hwndMain)) {
\r
8887 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8890 (void) ReleaseDC(hwndMain, hdc);
\r
8892 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8897 LoadGameTimerRunning()
\r
8899 return loadGameTimerEvent != 0;
\r
8903 StopLoadGameTimer()
\r
8905 if (loadGameTimerEvent == 0) return FALSE;
\r
8906 KillTimer(hwndMain, loadGameTimerEvent);
\r
8907 loadGameTimerEvent = 0;
\r
8912 StartLoadGameTimer(long millisec)
\r
8914 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8915 (UINT) millisec, NULL);
\r
8923 char fileTitle[MSG_SIZ];
\r
8925 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8926 f = OpenFileDialog(hwndMain, "a", defName,
\r
8927 appData.oldSaveStyle ? "gam" : "pgn",
\r
8929 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8931 SaveGame(f, 0, "");
\r
8938 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8940 if (delayedTimerEvent != 0) {
\r
8941 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8942 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8944 KillTimer(hwndMain, delayedTimerEvent);
\r
8945 delayedTimerEvent = 0;
\r
8946 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8947 delayedTimerCallback();
\r
8949 delayedTimerCallback = cb;
\r
8950 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8951 (UINT) millisec, NULL);
\r
8954 DelayedEventCallback
\r
8957 if (delayedTimerEvent) {
\r
8958 return delayedTimerCallback;
\r
8965 CancelDelayedEvent()
\r
8967 if (delayedTimerEvent) {
\r
8968 KillTimer(hwndMain, delayedTimerEvent);
\r
8969 delayedTimerEvent = 0;
\r
8973 DWORD GetWin32Priority(int nice)
\r
8974 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8976 REALTIME_PRIORITY_CLASS 0x00000100
\r
8977 HIGH_PRIORITY_CLASS 0x00000080
\r
8978 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8979 NORMAL_PRIORITY_CLASS 0x00000020
\r
8980 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8981 IDLE_PRIORITY_CLASS 0x00000040
\r
8983 if (nice < -15) return 0x00000080;
\r
8984 if (nice < 0) return 0x00008000;
\r
8985 if (nice == 0) return 0x00000020;
\r
8986 if (nice < 15) return 0x00004000;
\r
8987 return 0x00000040;
\r
8990 void RunCommand(char *cmdLine)
\r
8992 /* Now create the child process. */
\r
8993 STARTUPINFO siStartInfo;
\r
8994 PROCESS_INFORMATION piProcInfo;
\r
8996 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8997 siStartInfo.lpReserved = NULL;
\r
8998 siStartInfo.lpDesktop = NULL;
\r
8999 siStartInfo.lpTitle = NULL;
\r
9000 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9001 siStartInfo.cbReserved2 = 0;
\r
9002 siStartInfo.lpReserved2 = NULL;
\r
9003 siStartInfo.hStdInput = NULL;
\r
9004 siStartInfo.hStdOutput = NULL;
\r
9005 siStartInfo.hStdError = NULL;
\r
9007 CreateProcess(NULL,
\r
9008 cmdLine, /* command line */
\r
9009 NULL, /* process security attributes */
\r
9010 NULL, /* primary thread security attrs */
\r
9011 TRUE, /* handles are inherited */
\r
9012 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9013 NULL, /* use parent's environment */
\r
9015 &siStartInfo, /* STARTUPINFO pointer */
\r
9016 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9018 CloseHandle(piProcInfo.hThread);
\r
9021 /* Start a child process running the given program.
\r
9022 The process's standard output can be read from "from", and its
\r
9023 standard input can be written to "to".
\r
9024 Exit with fatal error if anything goes wrong.
\r
9025 Returns an opaque pointer that can be used to destroy the process
\r
9029 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
9031 #define BUFSIZE 4096
\r
9033 HANDLE hChildStdinRd, hChildStdinWr,
\r
9034 hChildStdoutRd, hChildStdoutWr;
\r
9035 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
9036 SECURITY_ATTRIBUTES saAttr;
\r
9038 PROCESS_INFORMATION piProcInfo;
\r
9039 STARTUPINFO siStartInfo;
\r
9041 char buf[MSG_SIZ];
\r
9044 if (appData.debugMode) {
\r
9045 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
9050 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
9051 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
9052 saAttr.bInheritHandle = TRUE;
\r
9053 saAttr.lpSecurityDescriptor = NULL;
\r
9056 * The steps for redirecting child's STDOUT:
\r
9057 * 1. Create anonymous pipe to be STDOUT for child.
\r
9058 * 2. Create a noninheritable duplicate of read handle,
\r
9059 * and close the inheritable read handle.
\r
9062 /* Create a pipe for the child's STDOUT. */
\r
9063 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
9064 return GetLastError();
\r
9067 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
9068 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
9069 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
9070 FALSE, /* not inherited */
\r
9071 DUPLICATE_SAME_ACCESS);
\r
9073 return GetLastError();
\r
9075 CloseHandle(hChildStdoutRd);
\r
9078 * The steps for redirecting child's STDIN:
\r
9079 * 1. Create anonymous pipe to be STDIN for child.
\r
9080 * 2. Create a noninheritable duplicate of write handle,
\r
9081 * and close the inheritable write handle.
\r
9084 /* Create a pipe for the child's STDIN. */
\r
9085 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
9086 return GetLastError();
\r
9089 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
9090 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
9091 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
9092 FALSE, /* not inherited */
\r
9093 DUPLICATE_SAME_ACCESS);
\r
9095 return GetLastError();
\r
9097 CloseHandle(hChildStdinWr);
\r
9099 /* Arrange to (1) look in dir for the child .exe file, and
\r
9100 * (2) have dir be the child's working directory. Interpret
\r
9101 * dir relative to the directory WinBoard loaded from. */
\r
9102 GetCurrentDirectory(MSG_SIZ, buf);
\r
9103 SetCurrentDirectory(installDir);
\r
9104 SetCurrentDirectory(dir);
\r
9106 /* Now create the child process. */
\r
9108 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9109 siStartInfo.lpReserved = NULL;
\r
9110 siStartInfo.lpDesktop = NULL;
\r
9111 siStartInfo.lpTitle = NULL;
\r
9112 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9113 siStartInfo.cbReserved2 = 0;
\r
9114 siStartInfo.lpReserved2 = NULL;
\r
9115 siStartInfo.hStdInput = hChildStdinRd;
\r
9116 siStartInfo.hStdOutput = hChildStdoutWr;
\r
9117 siStartInfo.hStdError = hChildStdoutWr;
\r
9119 fSuccess = CreateProcess(NULL,
\r
9120 cmdLine, /* command line */
\r
9121 NULL, /* process security attributes */
\r
9122 NULL, /* primary thread security attrs */
\r
9123 TRUE, /* handles are inherited */
\r
9124 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9125 NULL, /* use parent's environment */
\r
9127 &siStartInfo, /* STARTUPINFO pointer */
\r
9128 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9130 err = GetLastError();
\r
9131 SetCurrentDirectory(buf); /* return to prev directory */
\r
9136 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
9137 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
9138 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
9141 /* Close the handles we don't need in the parent */
\r
9142 CloseHandle(piProcInfo.hThread);
\r
9143 CloseHandle(hChildStdinRd);
\r
9144 CloseHandle(hChildStdoutWr);
\r
9146 /* Prepare return value */
\r
9147 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9148 cp->kind = CPReal;
\r
9149 cp->hProcess = piProcInfo.hProcess;
\r
9150 cp->pid = piProcInfo.dwProcessId;
\r
9151 cp->hFrom = hChildStdoutRdDup;
\r
9152 cp->hTo = hChildStdinWrDup;
\r
9154 *pr = (void *) cp;
\r
9156 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
9157 2000 where engines sometimes don't see the initial command(s)
\r
9158 from WinBoard and hang. I don't understand how that can happen,
\r
9159 but the Sleep is harmless, so I've put it in. Others have also
\r
9160 reported what may be the same problem, so hopefully this will fix
\r
9161 it for them too. */
\r
9169 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
9171 ChildProc *cp; int result;
\r
9173 cp = (ChildProc *) pr;
\r
9174 if (cp == NULL) return;
\r
9176 switch (cp->kind) {
\r
9178 /* TerminateProcess is considered harmful, so... */
\r
9179 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9180 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9181 /* The following doesn't work because the chess program
\r
9182 doesn't "have the same console" as WinBoard. Maybe
\r
9183 we could arrange for this even though neither WinBoard
\r
9184 nor the chess program uses a console for stdio? */
\r
9185 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9187 /* [AS] Special termination modes for misbehaving programs... */
\r
9188 if( signal == 9 ) {
\r
9189 result = TerminateProcess( cp->hProcess, 0 );
\r
9191 if ( appData.debugMode) {
\r
9192 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9195 else if( signal == 10 ) {
\r
9196 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
9198 if( dw != WAIT_OBJECT_0 ) {
\r
9199 result = TerminateProcess( cp->hProcess, 0 );
\r
9201 if ( appData.debugMode) {
\r
9202 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9208 CloseHandle(cp->hProcess);
\r
9212 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9216 closesocket(cp->sock);
\r
9221 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9222 closesocket(cp->sock);
\r
9223 closesocket(cp->sock2);
\r
9231 InterruptChildProcess(ProcRef pr)
\r
9235 cp = (ChildProc *) pr;
\r
9236 if (cp == NULL) return;
\r
9237 switch (cp->kind) {
\r
9239 /* The following doesn't work because the chess program
\r
9240 doesn't "have the same console" as WinBoard. Maybe
\r
9241 we could arrange for this even though neither WinBoard
\r
9242 nor the chess program uses a console for stdio */
\r
9243 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9248 /* Can't interrupt */
\r
9252 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9259 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9261 char cmdLine[MSG_SIZ];
\r
9263 if (port[0] == NULLCHAR) {
\r
9264 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9266 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9268 return StartChildProcess(cmdLine, "", pr);
\r
9272 /* Code to open TCP sockets */
\r
9275 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9281 struct sockaddr_in sa, mysa;
\r
9282 struct hostent FAR *hp;
\r
9283 unsigned short uport;
\r
9284 WORD wVersionRequested;
\r
9287 /* Initialize socket DLL */
\r
9288 wVersionRequested = MAKEWORD(1, 1);
\r
9289 err = WSAStartup(wVersionRequested, &wsaData);
\r
9290 if (err != 0) return err;
\r
9293 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9294 err = WSAGetLastError();
\r
9299 /* Bind local address using (mostly) don't-care values.
\r
9301 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9302 mysa.sin_family = AF_INET;
\r
9303 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9304 uport = (unsigned short) 0;
\r
9305 mysa.sin_port = htons(uport);
\r
9306 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9307 == SOCKET_ERROR) {
\r
9308 err = WSAGetLastError();
\r
9313 /* Resolve remote host name */
\r
9314 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9315 if (!(hp = gethostbyname(host))) {
\r
9316 unsigned int b0, b1, b2, b3;
\r
9318 err = WSAGetLastError();
\r
9320 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9321 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9322 hp->h_addrtype = AF_INET;
\r
9324 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9325 hp->h_addr_list[0] = (char *) malloc(4);
\r
9326 hp->h_addr_list[0][0] = (char) b0;
\r
9327 hp->h_addr_list[0][1] = (char) b1;
\r
9328 hp->h_addr_list[0][2] = (char) b2;
\r
9329 hp->h_addr_list[0][3] = (char) b3;
\r
9335 sa.sin_family = hp->h_addrtype;
\r
9336 uport = (unsigned short) atoi(port);
\r
9337 sa.sin_port = htons(uport);
\r
9338 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9340 /* Make connection */
\r
9341 if (connect(s, (struct sockaddr *) &sa,
\r
9342 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9343 err = WSAGetLastError();
\r
9348 /* Prepare return value */
\r
9349 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9350 cp->kind = CPSock;
\r
9352 *pr = (ProcRef *) cp;
\r
9358 OpenCommPort(char *name, ProcRef *pr)
\r
9363 char fullname[MSG_SIZ];
\r
9365 if (*name != '\\')
\r
9366 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9368 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9370 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9371 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9372 if (h == (HANDLE) -1) {
\r
9373 return GetLastError();
\r
9377 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9379 /* Accumulate characters until a 100ms pause, then parse */
\r
9380 ct.ReadIntervalTimeout = 100;
\r
9381 ct.ReadTotalTimeoutMultiplier = 0;
\r
9382 ct.ReadTotalTimeoutConstant = 0;
\r
9383 ct.WriteTotalTimeoutMultiplier = 0;
\r
9384 ct.WriteTotalTimeoutConstant = 0;
\r
9385 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9387 /* Prepare return value */
\r
9388 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9389 cp->kind = CPComm;
\r
9392 *pr = (ProcRef *) cp;
\r
9398 OpenLoopback(ProcRef *pr)
\r
9400 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9406 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9411 struct sockaddr_in sa, mysa;
\r
9412 struct hostent FAR *hp;
\r
9413 unsigned short uport;
\r
9414 WORD wVersionRequested;
\r
9417 char stderrPortStr[MSG_SIZ];
\r
9419 /* Initialize socket DLL */
\r
9420 wVersionRequested = MAKEWORD(1, 1);
\r
9421 err = WSAStartup(wVersionRequested, &wsaData);
\r
9422 if (err != 0) return err;
\r
9424 /* Resolve remote host name */
\r
9425 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9426 if (!(hp = gethostbyname(host))) {
\r
9427 unsigned int b0, b1, b2, b3;
\r
9429 err = WSAGetLastError();
\r
9431 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9432 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9433 hp->h_addrtype = AF_INET;
\r
9435 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9436 hp->h_addr_list[0] = (char *) malloc(4);
\r
9437 hp->h_addr_list[0][0] = (char) b0;
\r
9438 hp->h_addr_list[0][1] = (char) b1;
\r
9439 hp->h_addr_list[0][2] = (char) b2;
\r
9440 hp->h_addr_list[0][3] = (char) b3;
\r
9446 sa.sin_family = hp->h_addrtype;
\r
9447 uport = (unsigned short) 514;
\r
9448 sa.sin_port = htons(uport);
\r
9449 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9451 /* Bind local socket to unused "privileged" port address
\r
9453 s = INVALID_SOCKET;
\r
9454 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9455 mysa.sin_family = AF_INET;
\r
9456 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9457 for (fromPort = 1023;; fromPort--) {
\r
9458 if (fromPort < 0) {
\r
9460 return WSAEADDRINUSE;
\r
9462 if (s == INVALID_SOCKET) {
\r
9463 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9464 err = WSAGetLastError();
\r
9469 uport = (unsigned short) fromPort;
\r
9470 mysa.sin_port = htons(uport);
\r
9471 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9472 == SOCKET_ERROR) {
\r
9473 err = WSAGetLastError();
\r
9474 if (err == WSAEADDRINUSE) continue;
\r
9478 if (connect(s, (struct sockaddr *) &sa,
\r
9479 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9480 err = WSAGetLastError();
\r
9481 if (err == WSAEADDRINUSE) {
\r
9492 /* Bind stderr local socket to unused "privileged" port address
\r
9494 s2 = INVALID_SOCKET;
\r
9495 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9496 mysa.sin_family = AF_INET;
\r
9497 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9498 for (fromPort = 1023;; fromPort--) {
\r
9499 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9500 if (fromPort < 0) {
\r
9501 (void) closesocket(s);
\r
9503 return WSAEADDRINUSE;
\r
9505 if (s2 == INVALID_SOCKET) {
\r
9506 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9507 err = WSAGetLastError();
\r
9513 uport = (unsigned short) fromPort;
\r
9514 mysa.sin_port = htons(uport);
\r
9515 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9516 == SOCKET_ERROR) {
\r
9517 err = WSAGetLastError();
\r
9518 if (err == WSAEADDRINUSE) continue;
\r
9519 (void) closesocket(s);
\r
9523 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9524 err = WSAGetLastError();
\r
9525 if (err == WSAEADDRINUSE) {
\r
9527 s2 = INVALID_SOCKET;
\r
9530 (void) closesocket(s);
\r
9531 (void) closesocket(s2);
\r
9537 prevStderrPort = fromPort; // remember port used
\r
9538 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9540 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9541 err = WSAGetLastError();
\r
9542 (void) closesocket(s);
\r
9543 (void) closesocket(s2);
\r
9548 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9549 err = WSAGetLastError();
\r
9550 (void) closesocket(s);
\r
9551 (void) closesocket(s2);
\r
9555 if (*user == NULLCHAR) user = UserName();
\r
9556 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9557 err = WSAGetLastError();
\r
9558 (void) closesocket(s);
\r
9559 (void) closesocket(s2);
\r
9563 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9564 err = WSAGetLastError();
\r
9565 (void) closesocket(s);
\r
9566 (void) closesocket(s2);
\r
9571 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9572 err = WSAGetLastError();
\r
9573 (void) closesocket(s);
\r
9574 (void) closesocket(s2);
\r
9578 (void) closesocket(s2); /* Stop listening */
\r
9580 /* Prepare return value */
\r
9581 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9582 cp->kind = CPRcmd;
\r
9585 *pr = (ProcRef *) cp;
\r
9592 AddInputSource(ProcRef pr, int lineByLine,
\r
9593 InputCallback func, VOIDSTAR closure)
\r
9595 InputSource *is, *is2 = NULL;
\r
9596 ChildProc *cp = (ChildProc *) pr;
\r
9598 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9599 is->lineByLine = lineByLine;
\r
9601 is->closure = closure;
\r
9602 is->second = NULL;
\r
9603 is->next = is->buf;
\r
9604 if (pr == NoProc) {
\r
9605 is->kind = CPReal;
\r
9606 consoleInputSource = is;
\r
9608 is->kind = cp->kind;
\r
9610 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9611 we create all threads suspended so that the is->hThread variable can be
\r
9612 safely assigned, then let the threads start with ResumeThread.
\r
9614 switch (cp->kind) {
\r
9616 is->hFile = cp->hFrom;
\r
9617 cp->hFrom = NULL; /* now owned by InputThread */
\r
9619 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9620 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9624 is->hFile = cp->hFrom;
\r
9625 cp->hFrom = NULL; /* now owned by InputThread */
\r
9627 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9628 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9632 is->sock = cp->sock;
\r
9634 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9635 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9639 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9641 is->sock = cp->sock;
\r
9643 is2->sock = cp->sock2;
\r
9644 is2->second = is2;
\r
9646 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9647 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9649 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9650 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9654 if( is->hThread != NULL ) {
\r
9655 ResumeThread( is->hThread );
\r
9658 if( is2 != NULL && is2->hThread != NULL ) {
\r
9659 ResumeThread( is2->hThread );
\r
9663 return (InputSourceRef) is;
\r
9667 RemoveInputSource(InputSourceRef isr)
\r
9671 is = (InputSource *) isr;
\r
9672 is->hThread = NULL; /* tell thread to stop */
\r
9673 CloseHandle(is->hThread);
\r
9674 if (is->second != NULL) {
\r
9675 is->second->hThread = NULL;
\r
9676 CloseHandle(is->second->hThread);
\r
9680 int no_wrap(char *message, int count)
\r
9682 ConsoleOutput(message, count, FALSE);
\r
9687 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9690 int outCount = SOCKET_ERROR;
\r
9691 ChildProc *cp = (ChildProc *) pr;
\r
9692 static OVERLAPPED ovl;
\r
9693 static int line = 0;
\r
9697 if (appData.noJoin || !appData.useInternalWrap)
\r
9698 return no_wrap(message, count);
\r
9701 int width = get_term_width();
\r
9702 int len = wrap(NULL, message, count, width, &line);
\r
9703 char *msg = malloc(len);
\r
9707 return no_wrap(message, count);
\r
9710 dbgchk = wrap(msg, message, count, width, &line);
\r
9711 if (dbgchk != len && appData.debugMode)
\r
9712 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9713 ConsoleOutput(msg, len, FALSE);
\r
9720 if (ovl.hEvent == NULL) {
\r
9721 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9723 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9725 switch (cp->kind) {
\r
9728 outCount = send(cp->sock, message, count, 0);
\r
9729 if (outCount == SOCKET_ERROR) {
\r
9730 *outError = WSAGetLastError();
\r
9732 *outError = NO_ERROR;
\r
9737 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9738 &dOutCount, NULL)) {
\r
9739 *outError = NO_ERROR;
\r
9740 outCount = (int) dOutCount;
\r
9742 *outError = GetLastError();
\r
9747 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9748 &dOutCount, &ovl);
\r
9749 if (*outError == NO_ERROR) {
\r
9750 outCount = (int) dOutCount;
\r
9760 if(n != 0) Sleep(n);
\r
9764 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9767 /* Ignore delay, not implemented for WinBoard */
\r
9768 return OutputToProcess(pr, message, count, outError);
\r
9773 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9774 char *buf, int count, int error)
\r
9776 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9779 /* see wgamelist.c for Game List functions */
\r
9780 /* see wedittags.c for Edit Tags functions */
\r
9787 char buf[MSG_SIZ];
\r
9790 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9791 f = fopen(buf, "r");
\r
9793 ProcessICSInitScript(f);
\r
9803 StartAnalysisClock()
\r
9805 if (analysisTimerEvent) return;
\r
9806 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9807 (UINT) 2000, NULL);
\r
9811 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9813 highlightInfo.sq[0].x = fromX;
\r
9814 highlightInfo.sq[0].y = fromY;
\r
9815 highlightInfo.sq[1].x = toX;
\r
9816 highlightInfo.sq[1].y = toY;
\r
9822 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9823 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9827 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9829 premoveHighlightInfo.sq[0].x = fromX;
\r
9830 premoveHighlightInfo.sq[0].y = fromY;
\r
9831 premoveHighlightInfo.sq[1].x = toX;
\r
9832 premoveHighlightInfo.sq[1].y = toY;
\r
9836 ClearPremoveHighlights()
\r
9838 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9839 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9843 ShutDownFrontEnd()
\r
9845 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9846 DeleteClipboardTempFiles();
\r
9852 if (IsIconic(hwndMain))
\r
9853 ShowWindow(hwndMain, SW_RESTORE);
\r
9855 SetActiveWindow(hwndMain);
\r
9859 * Prototypes for animation support routines
\r
9861 static void ScreenSquare(int column, int row, POINT * pt);
\r
9862 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9863 POINT frames[], int * nFrames);
\r
9869 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9870 { // [HGM] atomic: animate blast wave
\r
9873 explodeInfo.fromX = fromX;
\r
9874 explodeInfo.fromY = fromY;
\r
9875 explodeInfo.toX = toX;
\r
9876 explodeInfo.toY = toY;
\r
9877 for(i=1; i<4*kFactor; i++) {
\r
9878 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9879 DrawPosition(FALSE, board);
\r
9880 Sleep(appData.animSpeed);
\r
9882 explodeInfo.radius = 0;
\r
9883 DrawPosition(TRUE, board);
\r
9887 AnimateMove(board, fromX, fromY, toX, toY)
\r
9894 ChessSquare piece;
\r
9895 POINT start, finish, mid;
\r
9896 POINT frames[kFactor * 2 + 1];
\r
9899 if (!appData.animate) return;
\r
9900 if (doingSizing) return;
\r
9901 if (fromY < 0 || fromX < 0) return;
\r
9902 piece = board[fromY][fromX];
\r
9903 if (piece >= EmptySquare) return;
\r
9905 ScreenSquare(fromX, fromY, &start);
\r
9906 ScreenSquare(toX, toY, &finish);
\r
9908 /* All moves except knight jumps move in straight line */
\r
9909 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9910 mid.x = start.x + (finish.x - start.x) / 2;
\r
9911 mid.y = start.y + (finish.y - start.y) / 2;
\r
9913 /* Knight: make straight movement then diagonal */
\r
9914 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9915 mid.x = start.x + (finish.x - start.x) / 2;
\r
9919 mid.y = start.y + (finish.y - start.y) / 2;
\r
9923 /* Don't use as many frames for very short moves */
\r
9924 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9925 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9927 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9929 animInfo.from.x = fromX;
\r
9930 animInfo.from.y = fromY;
\r
9931 animInfo.to.x = toX;
\r
9932 animInfo.to.y = toY;
\r
9933 animInfo.lastpos = start;
\r
9934 animInfo.piece = piece;
\r
9935 for (n = 0; n < nFrames; n++) {
\r
9936 animInfo.pos = frames[n];
\r
9937 DrawPosition(FALSE, NULL);
\r
9938 animInfo.lastpos = animInfo.pos;
\r
9939 Sleep(appData.animSpeed);
\r
9941 animInfo.pos = finish;
\r
9942 DrawPosition(FALSE, NULL);
\r
9943 animInfo.piece = EmptySquare;
\r
9944 Explode(board, fromX, fromY, toX, toY);
\r
9947 /* Convert board position to corner of screen rect and color */
\r
9950 ScreenSquare(column, row, pt)
\r
9951 int column; int row; POINT * pt;
\r
9954 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
9955 pt->y = lineGap + row * (squareSize + lineGap) + border;
\r
9957 pt->x = lineGap + column * (squareSize + lineGap) + border;
\r
9958 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
9962 /* Generate a series of frame coords from start->mid->finish.
\r
9963 The movement rate doubles until the half way point is
\r
9964 reached, then halves back down to the final destination,
\r
9965 which gives a nice slow in/out effect. The algorithmn
\r
9966 may seem to generate too many intermediates for short
\r
9967 moves, but remember that the purpose is to attract the
\r
9968 viewers attention to the piece about to be moved and
\r
9969 then to where it ends up. Too few frames would be less
\r
9973 Tween(start, mid, finish, factor, frames, nFrames)
\r
9974 POINT * start; POINT * mid;
\r
9975 POINT * finish; int factor;
\r
9976 POINT frames[]; int * nFrames;
\r
9978 int n, fraction = 1, count = 0;
\r
9980 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9981 for (n = 0; n < factor; n++)
\r
9983 for (n = 0; n < factor; n++) {
\r
9984 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9985 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9987 fraction = fraction / 2;
\r
9991 frames[count] = *mid;
\r
9994 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9996 for (n = 0; n < factor; n++) {
\r
9997 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9998 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
10000 fraction = fraction * 2;
\r
10002 *nFrames = count;
\r
10006 SettingsPopUp(ChessProgramState *cps)
\r
10007 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
10008 EngineOptionsPopup(savedHwnd, cps);
\r
10011 int flock(int fid, int code)
\r
10013 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
10015 ov.hEvent = NULL;
\r
10017 ov.OffsetHigh = 0;
\r
10019 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
10020 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
10021 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
10022 default: return -1;
\r
10031 static char col[8][20];
\r
10032 COLORREF color = *(COLORREF *) colorVariable[n];
\r
10034 snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
10039 ActivateTheme (int new)
\r
10040 { // Redo initialization of features depending on options that can occur in themes
\r
10042 if(new) InitDrawingColors();
\r
10043 fontBitmapSquareSize = 0; // request creation of new font pieces
\r
10044 InitDrawingSizes(boardSize, 0);
\r
10045 InvalidateRect(hwndMain, NULL, TRUE);
\r