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 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;
\r
229 int highTextStart[2], highTextEnd[2];
\r
230 extern RECT boardRect;
\r
233 GetMemoLine(HWND hDlg, int x, int y)
\r
234 { // [HGM] pv: translate click to PV line, and load it for display
\r
236 int index, start, end, memo;
\r
239 pt.x = x; pt.y = y;
\r
240 memo = currentPV ? IDC_EngineMemo2 : IDC_EngineMemo1;
\r
241 index = SendDlgItemMessage( hDlg, memo, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
242 GetDlgItemText(hDlg, memo, buf, sizeof(buf));
\r
243 if(LoadMultiPV(x, y, buf, index, &start, &end, currentPV)) {
\r
245 SendMessage( outputField[currentPV][nMemo], EM_SETSEL, (WPARAM)start, (LPARAM)end );
\r
246 highTextStart[currentPV] = start; highTextEnd[currentPV] = end;
\r
247 SetFocus(outputField[currentPV][nMemo]);
\r
251 // front end. Actual printing of PV lines into the output field
\r
252 void InsertIntoMemo( int which, char * text, int where )
\r
254 SendMessage( outputField[which][nMemo], EM_SETSEL, where, where ); // [HGM] multivar: choose insertion point
\r
256 SendMessage( outputField[which][nMemo], EM_REPLACESEL, (WPARAM) FALSE, (LPARAM) text );
\r
257 if(where < highTextStart[which]) { // [HGM] multiPVdisplay: move highlighting
\r
258 int len = strlen(text);
\r
259 highTextStart[which] += len; highTextEnd[which] += len;
\r
260 SendMessage( outputField[which][nMemo], EM_SETSEL, highTextStart[which], highTextEnd[which] );
\r
264 // front end. Associates an icon with an output field ("control" in Windows jargon).
\r
265 // [HGM] let it find out the output field from the 'which' number by itself
\r
266 void SetIcon( int which, int field, int nIcon )
\r
270 SendMessage( outputField[which][field], STM_SETICON, (WPARAM) icons[nIcon], 0 );
\r
274 // front end wrapper for SetWindowText, taking control number in stead of handle
\r
275 void DoSetWindowText(int which, int field, char *s_label)
\r
277 SetWindowText( outputField[which][field], s_label );
\r
280 void SetEngineOutputTitle(char *title)
\r
282 SetWindowText( engineOutputDialog, title );
\r
285 // This seems pure front end
\r
286 LRESULT CALLBACK EngineOutputProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )
\r
288 static SnapData sd;
\r
291 case WM_INITDIALOG:
\r
292 if( engineOutputDialog == NULL ) {
\r
293 engineOutputDialog = hDlg;
\r
295 Translate(hDlg, DLG_EngineOutput);
\r
296 RestoreWindowPlacement( hDlg, &wpEngineOutput ); /* Restore window placement */
\r
298 ResizeWindowControls( windowMode );
\r
300 SendDlgItemMessage( hDlg, IDC_EngineMemo1, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );
\r
301 SendDlgItemMessage( hDlg, IDC_EngineMemo2, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );
\r
304 SendDlgItemMessage( engineOutputDialog, IDC_EngineMemo1, WM_SETFONT, (WPARAM)font[boardSize][MOVEHISTORY_FONT]->hf, MAKELPARAM(TRUE, 0 ));
\r
305 SendDlgItemMessage( engineOutputDialog, IDC_EngineMemo2, WM_SETFONT, (WPARAM)font[boardSize][MOVEHISTORY_FONT]->hf, MAKELPARAM(TRUE, 0 ));
\r
307 SetEngineState( 0, STATE_IDLE, "" );
\r
308 SetEngineState( 1, STATE_IDLE, "" );
\r
314 switch (LOWORD(wParam)) {
\r
316 EndDialog(hDlg, TRUE);
\r
320 EndDialog(hDlg, FALSE);
\r
330 MovePV(LOWORD(lParam) - boardRect.left, HIWORD(lParam) - boardRect.top, boardRect.bottom - boardRect.top);
\r
335 SendMessage( outputField[currentPV][nMemo], EM_SETSEL, 0, 0 );
\r
336 highTextStart[currentPV] = highTextEnd[currentPV] = 0;
\r
341 if( wParam == IDC_EngineMemo1 || wParam == IDC_EngineMemo2 ) {
\r
342 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
343 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL)) == 0 ) {
\r
344 shiftKey = (lpMF->wParam & MK_SHIFT) != 0; // [HGM] remember last shift status
\r
345 currentPV = (wParam == IDC_EngineMemo2);
\r
346 GetMemoLine(hDlg, LOWORD(lpMF->lParam), HIWORD(lpMF->lParam));
\r
351 case WM_GETMINMAXINFO:
\r
353 MINMAXINFO * mmi = (MINMAXINFO *) lParam;
\r
355 mmi->ptMinTrackSize.x = 100;
\r
356 mmi->ptMinTrackSize.y = 160;
\r
361 EngineOutputPopDown();
\r
365 ResizeWindowControls( windowMode );
\r
368 case WM_ENTERSIZEMOVE:
\r
369 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
372 return OnSizing( &sd, hDlg, wParam, lParam );
\r
375 return OnMoving( &sd, hDlg, wParam, lParam );
\r
377 case WM_EXITSIZEMOVE:
\r
378 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
385 void EngineOutputPopUp()
\r
388 static int needInit = TRUE;
\r
390 CheckMenuItem(GetMenu(hwndMain), IDM_ShowEngineOutput, MF_CHECKED);
\r
392 if( engineOutputDialog ) {
\r
393 SendMessage( engineOutputDialog, WM_INITDIALOG, 0, 0 );
\r
395 if( ! engineOutputDialogUp ) {
\r
396 ShowWindow(engineOutputDialog, SW_SHOW);
\r
400 lpProc = MakeProcInstance( (FARPROC) EngineOutputProc, hInst );
\r
402 /* Note to self: dialog must have the WS_VISIBLE style set, otherwise it's not shown! */
\r
403 CreateDialog( hInst, MAKEINTRESOURCE(DLG_EngineOutput), hwndMain, (DLGPROC)lpProc );
\r
405 FreeProcInstance(lpProc);
\r
408 // [HGM] displaced to after creation of dialog, to allow initialization of output fields
\r
410 InitializeEngineOutput();
\r
414 engineOutputDialogUp = TRUE;
\r
418 void EngineOutputPopDown()
\r
420 CheckMenuItem(GetMenu(hwndMain), IDM_ShowEngineOutput, MF_UNCHECKED);
\r
422 if( engineOutputDialog ) {
\r
423 ShowWindow(engineOutputDialog, SW_HIDE);
\r
426 engineOutputDialogUp = FALSE;
\r
429 // front end. [HGM] Takes handle of output control from table, so only number is passed
\r
430 void DoClearMemo(int which)
\r
432 SendMessage( outputField[which][nMemo], WM_SETTEXT, 0, (LPARAM) "" );
\r
435 // front end (because only other front-end wants to know)
\r
436 int EngineOutputIsUp()
\r
438 return engineOutputDialogUp;
\r
441 // front end, to give back-end access to engineOutputDialog
\r
442 int EngineOutputDialogExists()
\r
444 return engineOutputDialog != NULL;
\r