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, 2013, 2014, 2015,
\r
9 * 2016 Free Software Foundation, Inc.
\r
11 * ------------------------------------------------------------------------
\r
13 * GNU XBoard is free software: you can redistribute it and/or modify
\r
14 * it under the terms of the GNU General Public License as published by
\r
15 * the Free Software Foundation, either version 3 of the License, or (at
\r
16 * your option) any later version.
\r
18 * GNU XBoard is distributed in the hope that it will be useful, but
\r
19 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
21 * General Public License for more details.
\r
23 * You should have received a copy of the GNU General Public License
\r
24 * along with this program. If not, see http://www.gnu.org/licenses/. *
\r
26 *------------------------------------------------------------------------
\r
27 ** See the file ChangeLog for a revision history. */
\r
31 #include <windows.h> /* required for all Windows applications */
\r
32 #include <richedit.h>
\r
36 #include <commdlg.h>
\r
40 #include "frontend.h"
\r
41 #include "backend.h"
\r
42 #include "winboard.h"
\r
45 #include "engineoutput.h"
\r
47 /* Module variables */
\r
49 static BOOLEAN engineOutputDialogUp = FALSE;
\r
50 HICON icons[8]; // [HGM] this front-end array translates back-end icon indicator to handle
\r
51 HWND outputField[2][7]; // [HGM] front-end array to translate output field to window handle
\r
54 static HICON LoadIconEx( int id )
\r
56 return LoadImage( hInst, MAKEINTRESOURCE(id), IMAGE_ICON, ICON_SIZE, ICON_SIZE, 0 );
\r
59 // [HGM] the platform-dependent way of indicating where output should go is now all
\r
60 // concentrated here, where a table of platform-dependent handles are initialized.
\r
61 // This cleanses most other routines of front-end stuff, so they can go into the back end.
\r
62 static void InitializeEngineOutput()
\r
64 // [HGM] made this into a table, rather than separate global variables
\r
65 icons[nColorBlack] = LoadIconEx( IDI_BLACK_14 );
\r
66 icons[nColorWhite] = LoadIconEx( IDI_WHITE_14 );
\r
67 icons[nColorUnknown] = LoadIconEx( IDI_UNKNOWN_14 );
\r
68 icons[nClear] = LoadIconEx( IDI_TRANS_14 );
\r
69 icons[nPondering] = LoadIconEx( IDI_PONDER_14 );
\r
70 icons[nThinking] = LoadIconEx( IDI_CLOCK_14 );
\r
71 icons[nAnalyzing] = LoadIconEx( IDI_ANALYZE2_14 );
\r
73 // [HGM] also make a table of handles to output controls
\r
74 // Note that engineOutputDialog must be defined first!
\r
75 outputField[0][nColorIcon] = GetDlgItem( engineOutputDialog, IDC_Color1 );
\r
76 outputField[0][nLabel] = GetDlgItem( engineOutputDialog, IDC_EngineLabel1 );
\r
77 outputField[0][nStateIcon] = GetDlgItem( engineOutputDialog, IDC_StateIcon1 );
\r
78 outputField[0][nStateData] = GetDlgItem( engineOutputDialog, IDC_StateData1 );
\r
79 outputField[0][nLabelNPS] = GetDlgItem( engineOutputDialog, IDC_Engine1_NPS );
\r
80 outputField[0][nMemo] = GetDlgItem( engineOutputDialog, IDC_EngineMemo1 );
\r
82 outputField[1][nColorIcon] = GetDlgItem( engineOutputDialog, IDC_Color2 );
\r
83 outputField[1][nLabel] = GetDlgItem( engineOutputDialog, IDC_EngineLabel2 );
\r
84 outputField[1][nStateIcon] = GetDlgItem( engineOutputDialog, IDC_StateIcon2 );
\r
85 outputField[1][nStateData] = GetDlgItem( engineOutputDialog, IDC_StateData2 );
\r
86 outputField[1][nLabelNPS] = GetDlgItem( engineOutputDialog, IDC_Engine2_NPS );
\r
87 outputField[1][nMemo] = GetDlgItem( engineOutputDialog, IDC_EngineMemo2 );
\r
91 static void SetControlPos( HWND hDlg, int id, int x, int y, int width, int height )
\r
93 HWND hControl = GetDlgItem( hDlg, id );
\r
95 SetWindowPos( hControl, HWND_TOP, x, y, width, height, SWP_NOZORDER );
\r
98 #define HIDDEN_X 20000
\r
99 #define HIDDEN_Y 20000
\r
102 static void HideControl( HWND hDlg, int id )
\r
104 HWND hControl = GetDlgItem( hDlg, id );
\r
107 GetWindowRect( hControl, &rc );
\r
110 Avoid hiding an already hidden control, because that causes many
\r
111 unnecessary WM_ERASEBKGND messages!
\r
113 if( rc.left != HIDDEN_X || rc.top != HIDDEN_Y ) {
\r
114 SetControlPos( hDlg, id, 20000, 20000, 100, 100 );
\r
118 // front end, although we might make GetWindowRect front end instead
\r
119 static int GetControlWidth( HWND hDlg, int id )
\r
123 GetWindowRect( GetDlgItem( hDlg, id ), &rc );
\r
125 return rc.right - rc.left;
\r
129 static int GetControlHeight( HWND hDlg, int id )
\r
133 GetWindowRect( GetDlgItem( hDlg, id ), &rc );
\r
135 return rc.bottom - rc.top;
\r
138 static int GetHeaderHeight()
\r
140 int result = GetControlHeight( engineOutputDialog, IDC_EngineLabel1 );
\r
142 if( result < ICON_SIZE ) result = ICON_SIZE;
\r
147 // The size calculations should be backend? If setControlPos is a platform-dependent way of doing things,
\r
148 // a platform-independent wrapper for it should be supplied.
\r
149 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
151 int label_x = x + ICON_SIZE + H_MARGIN;
\r
152 int label_h = GetControlHeight( hDlg, IDC_EngineLabel1 );
\r
153 int label_y = y + ICON_SIZE - label_h;
\r
154 int nps_w = GetControlWidth( hDlg, IDC_Engine1_NPS );
\r
155 int nps_x = clientWidth - H_MARGIN - nps_w;
\r
156 int state_data_w = GetControlWidth( hDlg, IDC_StateData1 );
\r
157 int state_data_x = nps_x - H_MARGIN - state_data_w;
\r
158 int state_icon_x = state_data_x - ICON_SIZE - 2;
\r
159 int max_w = clientWidth - 2*H_MARGIN;
\r
160 int memo_y = y + ICON_SIZE + LABEL_V_DISTANCE;
\r
162 SetControlPos( hDlg, idColor, x, y, ICON_SIZE, ICON_SIZE );
\r
163 SetControlPos( hDlg, idEngineLabel, label_x, label_y, state_icon_x - label_x, label_h );
\r
164 SetControlPos( hDlg, idStateIcon, state_icon_x, y, ICON_SIZE, ICON_SIZE );
\r
165 SetControlPos( hDlg, idStateData, state_data_x, label_y, state_data_w, label_h );
\r
166 SetControlPos( hDlg, idNPS, nps_x, label_y, nps_w, label_h );
\r
167 SetControlPos( hDlg, idMemo, x, memo_y, max_w, memoHeight );
\r
170 // Also here some of the size calculations should go to the back end, and their actual application to a front-end routine
\r
171 void ResizeWindowControls( int mode )
\r
173 HWND hDlg = engineOutputDialog; // [HGM] used to be parameter, but routine is called from back-end
\r
175 int headerHeight = GetHeaderHeight();
\r
176 // int labelHeight = GetControlHeight( hDlg, IDC_EngineLabel1 );
\r
177 // int labelOffset = H_MARGIN + ICON_SIZE + H_MARGIN;
\r
178 // int labelDeltaY = ICON_SIZE - labelHeight;
\r
181 int maxControlWidth;
\r
184 /* Initialize variables */
\r
185 GetClientRect( hDlg, &rc );
\r
187 clientWidth = rc.right - rc.left;
\r
188 clientHeight = rc.bottom - rc.top;
\r
190 maxControlWidth = clientWidth - 2*H_MARGIN;
\r
192 npsWidth = GetControlWidth( hDlg, IDC_Engine1_NPS );
\r
194 /* Resize controls */
\r
197 PositionControlSet( hDlg, H_MARGIN, V_MARGIN,
\r
199 clientHeight - V_MARGIN - LABEL_V_DISTANCE - headerHeight- V_MARGIN,
\r
200 IDC_Color1, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineMemo1, IDC_StateIcon1, IDC_StateData1 );
\r
202 /* Hide controls for the second engine */
\r
203 HideControl( hDlg, IDC_Color2 );
\r
204 HideControl( hDlg, IDC_EngineLabel2 );
\r
205 HideControl( hDlg, IDC_StateIcon2 );
\r
206 HideControl( hDlg, IDC_StateData2 );
\r
207 HideControl( hDlg, IDC_Engine2_NPS );
\r
208 HideControl( hDlg, IDC_EngineMemo2 );
\r
209 SendDlgItemMessage( hDlg, IDC_EngineMemo2, WM_SETTEXT, 0, (LPARAM) "" );
\r
210 /* TODO: we should also hide/disable them!!! what about tab stops?!?! */
\r
214 int memo_h = (clientHeight - headerHeight*2 - V_MARGIN*2 - LABEL_V_DISTANCE*2 - SPLITTER_SIZE) / 2;
\r
215 int header1_y = V_MARGIN;
\r
216 int header2_y = V_MARGIN + headerHeight + LABEL_V_DISTANCE + memo_h + SPLITTER_SIZE;
\r
218 PositionControlSet( hDlg, H_MARGIN, header1_y, clientWidth, memo_h,
\r
219 IDC_Color1, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineMemo1, IDC_StateIcon1, IDC_StateData1 );
\r
221 PositionControlSet( hDlg, H_MARGIN, header2_y, clientWidth, memo_h,
\r
222 IDC_Color2, IDC_EngineLabel2, IDC_Engine2_NPS, IDC_EngineMemo2, IDC_StateIcon2, IDC_StateData2 );
\r
225 InvalidateRect( GetDlgItem(hDlg,IDC_EngineMemo1), NULL, FALSE );
\r
226 InvalidateRect( GetDlgItem(hDlg,IDC_EngineMemo2), NULL, FALSE );
\r
229 static int currentPV;
\r
230 int highTextStart[2], highTextEnd[2];
\r
231 extern RECT boardRect;
\r
234 GetMemoLine(HWND hDlg, int x, int y)
\r
235 { // [HGM] pv: translate click to PV line, and load it for display
\r
237 int index, start, end, memo;
\r
240 pt.x = x; pt.y = y;
\r
241 memo = currentPV ? IDC_EngineMemo2 : IDC_EngineMemo1;
\r
242 index = SendDlgItemMessage( hDlg, memo, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
243 GetDlgItemText(hDlg, memo, buf, sizeof(buf));
\r
244 if(LoadMultiPV(x, y, buf, index, &start, &end, currentPV)) {
\r
246 SendMessage( outputField[currentPV][nMemo], EM_SETSEL, (WPARAM)start, (LPARAM)end );
\r
247 highTextStart[currentPV] = start; highTextEnd[currentPV] = end;
\r
248 SetFocus(outputField[currentPV][nMemo]);
\r
252 // front end. Actual printing of PV lines into the output field
\r
253 void InsertIntoMemo( int which, char * text, int where )
\r
255 SendMessage( outputField[which][nMemo], EM_SETSEL, where, where ); // [HGM] multivar: choose insertion point
\r
257 SendMessage( outputField[which][nMemo], EM_REPLACESEL, (WPARAM) FALSE, (LPARAM) text );
\r
258 if(where < highTextStart[which]) { // [HGM] multiPVdisplay: move highlighting
\r
259 int len = strlen(text);
\r
260 highTextStart[which] += len; highTextEnd[which] += len;
\r
261 SendMessage( outputField[which][nMemo], EM_SETSEL, highTextStart[which], highTextEnd[which] );
\r
265 // front end. Associates an icon with an output field ("control" in Windows jargon).
\r
266 // [HGM] let it find out the output field from the 'which' number by itself
\r
267 void SetIcon( int which, int field, int nIcon )
\r
271 SendMessage( outputField[which][field], STM_SETICON, (WPARAM) icons[nIcon], 0 );
\r
275 // front end wrapper for SetWindowText, taking control number in stead of handle
\r
276 void DoSetWindowText(int which, int field, char *s_label)
\r
278 SetWindowText( outputField[which][field], s_label );
\r
281 void SetEngineOutputTitle(char *title)
\r
283 SetWindowText( engineOutputDialog, title );
\r
286 // This seems pure front end
\r
287 LRESULT CALLBACK EngineOutputProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )
\r
289 static SnapData sd;
\r
292 case WM_INITDIALOG:
\r
293 if( engineOutputDialog == NULL ) {
\r
294 engineOutputDialog = hDlg;
\r
296 Translate(hDlg, DLG_EngineOutput);
\r
297 RestoreWindowPlacement( hDlg, &wpEngineOutput ); /* Restore window placement */
\r
299 ResizeWindowControls( windowMode );
\r
301 SendDlgItemMessage( hDlg, IDC_EngineMemo1, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );
\r
302 SendDlgItemMessage( hDlg, IDC_EngineMemo2, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );
\r
305 SendDlgItemMessage( engineOutputDialog, IDC_EngineMemo1, WM_SETFONT, (WPARAM)font[boardSize][MOVEHISTORY_FONT]->hf, MAKELPARAM(TRUE, 0 ));
\r
306 SendDlgItemMessage( engineOutputDialog, IDC_EngineMemo2, WM_SETFONT, (WPARAM)font[boardSize][MOVEHISTORY_FONT]->hf, MAKELPARAM(TRUE, 0 ));
\r
308 SetEngineState( 0, STATE_IDLE, "" );
\r
309 SetEngineState( 1, STATE_IDLE, "" );
\r
315 switch (LOWORD(wParam)) {
\r
317 EndDialog(hDlg, TRUE);
\r
321 EndDialog(hDlg, FALSE);
\r
331 MovePV(LOWORD(lParam) - boardRect.left, HIWORD(lParam) - boardRect.top, boardRect.bottom - boardRect.top);
\r
336 SendMessage( outputField[currentPV][nMemo], EM_SETSEL, 0, 0 );
\r
337 highTextStart[currentPV] = highTextEnd[currentPV] = 0;
\r
342 if( wParam == IDC_EngineMemo1 || wParam == IDC_EngineMemo2 ) {
\r
343 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
344 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL)) == 0 ) {
\r
345 shiftKey = (lpMF->wParam & MK_SHIFT) != 0; // [HGM] remember last shift status
\r
346 currentPV = (wParam == IDC_EngineMemo2);
\r
347 GetMemoLine(hDlg, LOWORD(lpMF->lParam), HIWORD(lpMF->lParam));
\r
352 case WM_GETMINMAXINFO:
\r
354 MINMAXINFO * mmi = (MINMAXINFO *) lParam;
\r
356 mmi->ptMinTrackSize.x = 100;
\r
357 mmi->ptMinTrackSize.y = 160;
\r
362 EngineOutputPopDown();
\r
366 ResizeWindowControls( windowMode );
\r
369 case WM_ENTERSIZEMOVE:
\r
370 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
373 return OnSizing( &sd, hDlg, wParam, lParam );
\r
376 return OnMoving( &sd, hDlg, wParam, lParam );
\r
378 case WM_EXITSIZEMOVE:
\r
379 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
386 void EngineOutputPopUp()
\r
389 static int needInit = TRUE;
\r
391 CheckMenuItem(GetMenu(hwndMain), IDM_ShowEngineOutput, MF_CHECKED);
\r
393 if( engineOutputDialog ) {
\r
394 SendMessage( engineOutputDialog, WM_INITDIALOG, 0, 0 );
\r
396 if( ! engineOutputDialogUp ) {
\r
397 ShowWindow(engineOutputDialog, SW_SHOW);
\r
401 lpProc = MakeProcInstance( (FARPROC) EngineOutputProc, hInst );
\r
403 /* Note to self: dialog must have the WS_VISIBLE style set, otherwise it's not shown! */
\r
404 CreateDialog( hInst, MAKEINTRESOURCE(DLG_EngineOutput), hwndMain, (DLGPROC)lpProc );
\r
406 FreeProcInstance(lpProc);
\r
409 // [HGM] displaced to after creation of dialog, to allow initialization of output fields
\r
411 InitializeEngineOutput();
\r
415 engineOutputDialogUp = TRUE;
\r
419 void EngineOutputPopDown()
\r
421 CheckMenuItem(GetMenu(hwndMain), IDM_ShowEngineOutput, MF_UNCHECKED);
\r
423 if( engineOutputDialog ) {
\r
424 ShowWindow(engineOutputDialog, SW_HIDE);
\r
427 engineOutputDialogUp = FALSE;
\r
430 // front end. [HGM] Takes handle of output control from table, so only number is passed
\r
431 void DoClearMemo(int which)
\r
433 SendMessage( outputField[which][nMemo], WM_SETTEXT, 0, (LPARAM) "" );
\r
436 // front end (because only other front-end wants to know)
\r
437 int EngineOutputIsUp()
\r
439 return engineOutputDialogUp;
\r
442 // front end, to give back-end access to engineOutputDialog
\r
443 int EngineOutputDialogExists()
\r
445 return engineOutputDialog != NULL;
\r