2 * wengineoutput.c - split-off front-end of Engine output (PV) by HGM
\r
4 * Author: Alessandro Scotti (Dec 2005)
\r
6 * Copyright 2005 Alessandro Scotti
\r
8 * Enhancements Copyright 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
\r
10 * ------------------------------------------------------------------------
\r
12 * GNU XBoard is free software: you can redistribute it and/or modify
\r
13 * it under the terms of the GNU General Public License as published by
\r
14 * the Free Software Foundation, either version 3 of the License, or (at
\r
15 * your option) any later version.
\r
17 * GNU XBoard is distributed in the hope that it will be useful, but
\r
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
20 * General Public License for more details.
\r
22 * You should have received a copy of the GNU General Public License
\r
23 * along with this program. If not, see http://www.gnu.org/licenses/. *
\r
25 *------------------------------------------------------------------------
\r
26 ** See the file ChangeLog for a revision history. */
\r
30 #include <windows.h> /* required for all Windows applications */
\r
31 #include <richedit.h>
\r
35 #include <commdlg.h>
\r
39 #include "frontend.h"
\r
40 #include "backend.h"
\r
41 #include "winboard.h"
\r
44 #include "engineoutput.h"
\r
46 /* Module variables */
\r
48 static BOOLEAN engineOutputDialogUp = FALSE;
\r
49 HICON icons[8]; // [HGM] this front-end array translates back-end icon indicator to handle
\r
50 HWND outputField[2][7]; // [HGM] front-end array to translate output field to window handle
\r
53 static HICON LoadIconEx( int id )
\r
55 return LoadImage( hInst, MAKEINTRESOURCE(id), IMAGE_ICON, ICON_SIZE, ICON_SIZE, 0 );
\r
58 // [HGM] the platform-dependent way of indicating where output should go is now all
\r
59 // concentrated here, where a table of platform-dependent handles are initialized.
\r
60 // This cleanses most other routines of front-end stuff, so they can go into the back end.
\r
61 static void InitializeEngineOutput()
\r
63 // [HGM] made this into a table, rather than separate global variables
\r
64 icons[nColorBlack] = LoadIconEx( IDI_BLACK_14 );
\r
65 icons[nColorWhite] = LoadIconEx( IDI_WHITE_14 );
\r
66 icons[nColorUnknown] = LoadIconEx( IDI_UNKNOWN_14 );
\r
67 icons[nClear] = LoadIconEx( IDI_TRANS_14 );
\r
68 icons[nPondering] = LoadIconEx( IDI_PONDER_14 );
\r
69 icons[nThinking] = LoadIconEx( IDI_CLOCK_14 );
\r
70 icons[nAnalyzing] = LoadIconEx( IDI_ANALYZE2_14 );
\r
72 // [HGM] also make a table of handles to output controls
\r
73 // Note that engineOutputDialog must be defined first!
\r
74 outputField[0][nColorIcon] = GetDlgItem( engineOutputDialog, IDC_Color1 );
\r
75 outputField[0][nLabel] = GetDlgItem( engineOutputDialog, IDC_EngineLabel1 );
\r
76 outputField[0][nStateIcon] = GetDlgItem( engineOutputDialog, IDC_StateIcon1 );
\r
77 outputField[0][nStateData] = GetDlgItem( engineOutputDialog, IDC_StateData1 );
\r
78 outputField[0][nLabelNPS] = GetDlgItem( engineOutputDialog, IDC_Engine1_NPS );
\r
79 outputField[0][nMemo] = GetDlgItem( engineOutputDialog, IDC_EngineMemo1 );
\r
81 outputField[1][nColorIcon] = GetDlgItem( engineOutputDialog, IDC_Color2 );
\r
82 outputField[1][nLabel] = GetDlgItem( engineOutputDialog, IDC_EngineLabel2 );
\r
83 outputField[1][nStateIcon] = GetDlgItem( engineOutputDialog, IDC_StateIcon2 );
\r
84 outputField[1][nStateData] = GetDlgItem( engineOutputDialog, IDC_StateData2 );
\r
85 outputField[1][nLabelNPS] = GetDlgItem( engineOutputDialog, IDC_Engine2_NPS );
\r
86 outputField[1][nMemo] = GetDlgItem( engineOutputDialog, IDC_EngineMemo2 );
\r
90 static void SetControlPos( HWND hDlg, int id, int x, int y, int width, int height )
\r
92 HWND hControl = GetDlgItem( hDlg, id );
\r
94 SetWindowPos( hControl, HWND_TOP, x, y, width, height, SWP_NOZORDER );
\r
97 #define HIDDEN_X 20000
\r
98 #define HIDDEN_Y 20000
\r
101 static void HideControl( HWND hDlg, int id )
\r
103 HWND hControl = GetDlgItem( hDlg, id );
\r
106 GetWindowRect( hControl, &rc );
\r
109 Avoid hiding an already hidden control, because that causes many
\r
110 unnecessary WM_ERASEBKGND messages!
\r
112 if( rc.left != HIDDEN_X || rc.top != HIDDEN_Y ) {
\r
113 SetControlPos( hDlg, id, 20000, 20000, 100, 100 );
\r
117 // front end, although we might make GetWindowRect front end instead
\r
118 static int GetControlWidth( HWND hDlg, int id )
\r
122 GetWindowRect( GetDlgItem( hDlg, id ), &rc );
\r
124 return rc.right - rc.left;
\r
128 static int GetControlHeight( HWND hDlg, int id )
\r
132 GetWindowRect( GetDlgItem( hDlg, id ), &rc );
\r
134 return rc.bottom - rc.top;
\r
137 static int GetHeaderHeight()
\r
139 int result = GetControlHeight( engineOutputDialog, IDC_EngineLabel1 );
\r
141 if( result < ICON_SIZE ) result = ICON_SIZE;
\r
146 // The size calculations should be backend? If setControlPos is a platform-dependent way of doing things,
\r
147 // a platform-independent wrapper for it should be supplied.
\r
148 static void PositionControlSet( HWND hDlg, int x, int y, int clientWidth, int memoHeight, int idColor, int idEngineLabel, int idNPS, int idMemo, int idStateIcon, int idStateData )
\r
150 int label_x = x + ICON_SIZE + H_MARGIN;
\r
151 int label_h = GetControlHeight( hDlg, IDC_EngineLabel1 );
\r
152 int label_y = y + ICON_SIZE - label_h;
\r
153 int nps_w = GetControlWidth( hDlg, IDC_Engine1_NPS );
\r
154 int nps_x = clientWidth - H_MARGIN - nps_w;
\r
155 int state_data_w = GetControlWidth( hDlg, IDC_StateData1 );
\r
156 int state_data_x = nps_x - H_MARGIN - state_data_w;
\r
157 int state_icon_x = state_data_x - ICON_SIZE - 2;
\r
158 int max_w = clientWidth - 2*H_MARGIN;
\r
159 int memo_y = y + ICON_SIZE + LABEL_V_DISTANCE;
\r
161 SetControlPos( hDlg, idColor, x, y, ICON_SIZE, ICON_SIZE );
\r
162 SetControlPos( hDlg, idEngineLabel, label_x, label_y, state_icon_x - label_x, label_h );
\r
163 SetControlPos( hDlg, idStateIcon, state_icon_x, y, ICON_SIZE, ICON_SIZE );
\r
164 SetControlPos( hDlg, idStateData, state_data_x, label_y, state_data_w, label_h );
\r
165 SetControlPos( hDlg, idNPS, nps_x, label_y, nps_w, label_h );
\r
166 SetControlPos( hDlg, idMemo, x, memo_y, max_w, memoHeight );
\r
169 // Also here some of the size calculations should go to the back end, and their actual application to a front-end routine
\r
170 void ResizeWindowControls( int mode )
\r
172 HWND hDlg = engineOutputDialog; // [HGM] used to be parameter, but routine is called from back-end
\r
174 int headerHeight = GetHeaderHeight();
\r
175 // int labelHeight = GetControlHeight( hDlg, IDC_EngineLabel1 );
\r
176 // int labelOffset = H_MARGIN + ICON_SIZE + H_MARGIN;
\r
177 // int labelDeltaY = ICON_SIZE - labelHeight;
\r
180 int maxControlWidth;
\r
183 /* Initialize variables */
\r
184 GetClientRect( hDlg, &rc );
\r
186 clientWidth = rc.right - rc.left;
\r
187 clientHeight = rc.bottom - rc.top;
\r
189 maxControlWidth = clientWidth - 2*H_MARGIN;
\r
191 npsWidth = GetControlWidth( hDlg, IDC_Engine1_NPS );
\r
193 /* Resize controls */
\r
196 PositionControlSet( hDlg, H_MARGIN, V_MARGIN,
\r
198 clientHeight - V_MARGIN - LABEL_V_DISTANCE - headerHeight- V_MARGIN,
\r
199 IDC_Color1, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineMemo1, IDC_StateIcon1, IDC_StateData1 );
\r
201 /* Hide controls for the second engine */
\r
202 HideControl( hDlg, IDC_Color2 );
\r
203 HideControl( hDlg, IDC_EngineLabel2 );
\r
204 HideControl( hDlg, IDC_StateIcon2 );
\r
205 HideControl( hDlg, IDC_StateData2 );
\r
206 HideControl( hDlg, IDC_Engine2_NPS );
\r
207 HideControl( hDlg, IDC_EngineMemo2 );
\r
208 SendDlgItemMessage( hDlg, IDC_EngineMemo2, WM_SETTEXT, 0, (LPARAM) "" );
\r
209 /* TODO: we should also hide/disable them!!! what about tab stops?!?! */
\r
213 int memo_h = (clientHeight - headerHeight*2 - V_MARGIN*2 - LABEL_V_DISTANCE*2 - SPLITTER_SIZE) / 2;
\r
214 int header1_y = V_MARGIN;
\r
215 int header2_y = V_MARGIN + headerHeight + LABEL_V_DISTANCE + memo_h + SPLITTER_SIZE;
\r
217 PositionControlSet( hDlg, H_MARGIN, header1_y, clientWidth, memo_h,
\r
218 IDC_Color1, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineMemo1, IDC_StateIcon1, IDC_StateData1 );
\r
220 PositionControlSet( hDlg, H_MARGIN, header2_y, clientWidth, memo_h,
\r
221 IDC_Color2, IDC_EngineLabel2, IDC_Engine2_NPS, IDC_EngineMemo2, IDC_StateIcon2, IDC_StateData2 );
\r
224 InvalidateRect( GetDlgItem(hDlg,IDC_EngineMemo1), NULL, FALSE );
\r
225 InvalidateRect( GetDlgItem(hDlg,IDC_EngineMemo2), NULL, FALSE );
\r
228 static int currentPV, highTextStart[2], highTextEnd[2];
\r
229 extern RECT boardRect;
\r
232 GetMemoLine(HWND hDlg, int x, int y)
\r
233 { // [HGM] pv: translate click to PV line, and load it for display
\r
235 int index, start, end, memo;
\r
238 pt.x = x; pt.y = y;
\r
239 memo = currentPV ? IDC_EngineMemo2 : IDC_EngineMemo1;
\r
240 index = SendDlgItemMessage( hDlg, memo, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
241 GetDlgItemText(hDlg, memo, buf, sizeof(buf));
\r
242 if(LoadMultiPV(x, y, buf, index, &start, &end, currentPV)) {
\r
244 SendMessage( outputField[currentPV][nMemo], EM_SETSEL, (WPARAM)start, (LPARAM)end );
\r
245 highTextStart[currentPV] = start; highTextEnd[currentPV] = end;
\r
246 SetFocus(outputField[currentPV][nMemo]);
\r
250 // front end. Actual printing of PV lines into the output field
\r
251 void InsertIntoMemo( int which, char * text, int where )
\r
253 SendMessage( outputField[which][nMemo], EM_SETSEL, where, where ); // [HGM] multivar: choose insertion point
\r
255 SendMessage( outputField[which][nMemo], EM_REPLACESEL, (WPARAM) FALSE, (LPARAM) text );
\r
256 if(where < highTextStart[which]) { // [HGM] multiPVdisplay: move highlighting
\r
257 int len = strlen(text);
\r
258 highTextStart[which] += len; highTextEnd[which] += len;
\r
259 SendMessage( outputField[which][nMemo], EM_SETSEL, highTextStart[which], highTextEnd[which] );
\r
263 // front end. Associates an icon with an output field ("control" in Windows jargon).
\r
264 // [HGM] let it find out the output field from the 'which' number by itself
\r
265 void SetIcon( int which, int field, int nIcon )
\r
269 SendMessage( outputField[which][field], STM_SETICON, (WPARAM) icons[nIcon], 0 );
\r
273 // front end wrapper for SetWindowText, taking control number in stead of handle
\r
274 void DoSetWindowText(int which, int field, char *s_label)
\r
276 SetWindowText( outputField[which][field], s_label );
\r
279 void SetEngineOutputTitle(char *title)
\r
281 SetWindowText( engineOutputDialog, title );
\r
284 // This seems pure front end
\r
285 LRESULT CALLBACK EngineOutputProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )
\r
287 static SnapData sd;
\r
290 case WM_INITDIALOG:
\r
291 if( engineOutputDialog == NULL ) {
\r
292 engineOutputDialog = hDlg;
\r
294 Translate(hDlg, DLG_EngineOutput);
\r
295 RestoreWindowPlacement( hDlg, &wpEngineOutput ); /* Restore window placement */
\r
297 ResizeWindowControls( windowMode );
\r
299 SendDlgItemMessage( hDlg, IDC_EngineMemo1, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );
\r
300 SendDlgItemMessage( hDlg, IDC_EngineMemo2, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );
\r
303 SendDlgItemMessage( engineOutputDialog, IDC_EngineMemo1, WM_SETFONT, (WPARAM)font[boardSize][MOVEHISTORY_FONT]->hf, MAKELPARAM(TRUE, 0 ));
\r
304 SendDlgItemMessage( engineOutputDialog, IDC_EngineMemo2, WM_SETFONT, (WPARAM)font[boardSize][MOVEHISTORY_FONT]->hf, MAKELPARAM(TRUE, 0 ));
\r
306 SetEngineState( 0, STATE_IDLE, "" );
\r
307 SetEngineState( 1, STATE_IDLE, "" );
\r
313 switch (LOWORD(wParam)) {
\r
315 EndDialog(hDlg, TRUE);
\r
319 EndDialog(hDlg, FALSE);
\r
329 MovePV(LOWORD(lParam) - boardRect.left, HIWORD(lParam) - boardRect.top, boardRect.bottom - boardRect.top);
\r
334 SendMessage( outputField[currentPV][nMemo], EM_SETSEL, 0, 0 );
\r
335 highTextStart[currentPV] = highTextEnd[currentPV] = 0;
\r
340 if( wParam == IDC_EngineMemo1 || wParam == IDC_EngineMemo2 ) {
\r
341 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
342 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL)) == 0 ) {
\r
343 shiftKey = (lpMF->wParam & MK_SHIFT) != 0; // [HGM] remember last shift status
\r
344 currentPV = (wParam == IDC_EngineMemo2);
\r
345 GetMemoLine(hDlg, LOWORD(lpMF->lParam), HIWORD(lpMF->lParam));
\r
350 case WM_GETMINMAXINFO:
\r
352 MINMAXINFO * mmi = (MINMAXINFO *) lParam;
\r
354 mmi->ptMinTrackSize.x = 100;
\r
355 mmi->ptMinTrackSize.y = 160;
\r
360 EngineOutputPopDown();
\r
364 ResizeWindowControls( windowMode );
\r
367 case WM_ENTERSIZEMOVE:
\r
368 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
371 return OnSizing( &sd, hDlg, wParam, lParam );
\r
374 return OnMoving( &sd, hDlg, wParam, lParam );
\r
376 case WM_EXITSIZEMOVE:
\r
377 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
384 void EngineOutputPopUp()
\r
387 static int needInit = TRUE;
\r
389 CheckMenuItem(GetMenu(hwndMain), IDM_ShowEngineOutput, MF_CHECKED);
\r
391 if( engineOutputDialog ) {
\r
392 SendMessage( engineOutputDialog, WM_INITDIALOG, 0, 0 );
\r
394 if( ! engineOutputDialogUp ) {
\r
395 ShowWindow(engineOutputDialog, SW_SHOW);
\r
399 lpProc = MakeProcInstance( (FARPROC) EngineOutputProc, hInst );
\r
401 /* Note to self: dialog must have the WS_VISIBLE style set, otherwise it's not shown! */
\r
402 CreateDialog( hInst, MAKEINTRESOURCE(DLG_EngineOutput), hwndMain, (DLGPROC)lpProc );
\r
404 FreeProcInstance(lpProc);
\r
407 // [HGM] displaced to after creation of dialog, to allow initialization of output fields
\r
409 InitializeEngineOutput();
\r
413 engineOutputDialogUp = TRUE;
\r
417 void EngineOutputPopDown()
\r
419 CheckMenuItem(GetMenu(hwndMain), IDM_ShowEngineOutput, MF_UNCHECKED);
\r
421 if( engineOutputDialog ) {
\r
422 ShowWindow(engineOutputDialog, SW_HIDE);
\r
425 engineOutputDialogUp = FALSE;
\r
428 // front end. [HGM] Takes handle of output control from table, so only number is passed
\r
429 void DoClearMemo(int which)
\r
431 SendMessage( outputField[which][nMemo], WM_SETTEXT, 0, (LPARAM) "" );
\r
434 // front end (because only other front-end wants to know)
\r
435 int EngineOutputIsUp()
\r
437 return engineOutputDialogUp;
\r
440 // front end, to give back-end access to engineOutputDialog
\r
441 int EngineOutputDialogExists()
\r
443 return engineOutputDialog != NULL;
\r