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 * ------------------------------------------------------------------------
\r
10 * GNU XBoard is free software: you can redistribute it and/or modify
\r
11 * it under the terms of the GNU General Public License as published by
\r
12 * the Free Software Foundation, either version 3 of the License, or (at
\r
13 * your option) any later version.
\r
15 * GNU XBoard is distributed in the hope that it will be useful, but
\r
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
18 * General Public License for more details.
\r
20 * You should have received a copy of the GNU General Public License
\r
21 * along with this program. If not, see http://www.gnu.org/licenses/. *
\r
23 *------------------------------------------------------------------------
\r
24 ** See the file ChangeLog for a revision history. */
\r
28 #include <windows.h> /* required for all Windows applications */
\r
29 #include <richedit.h>
\r
33 #include <commdlg.h>
\r
37 #include "frontend.h"
\r
38 #include "backend.h"
\r
39 #include "winboard.h"
\r
42 #include "engineoutput.h"
\r
44 /* Module variables */
\r
46 static BOOLEAN engineOutputDialogUp = FALSE;
\r
47 HICON icons[8]; // [HGM] this front-end array translates back-end icon indicator to handle
\r
48 HWND outputField[2][7]; // [HGM] front-end array to translate output field to window handle
\r
51 static HICON LoadIconEx( int id )
\r
53 return LoadImage( hInst, MAKEINTRESOURCE(id), IMAGE_ICON, ICON_SIZE, ICON_SIZE, 0 );
\r
56 // [HGM] the platform-dependent way of indicating where output should go is now all
\r
57 // concentrated here, where a table of platform-dependent handles are initialized.
\r
58 // This cleanses most other routines of front-end stuff, so they can go into the back end.
\r
59 static void InitializeEngineOutput()
\r
61 // [HGM] made this into a table, rather than separate global variables
\r
62 icons[nColorBlack] = LoadIconEx( IDI_BLACK_14 );
\r
63 icons[nColorWhite] = LoadIconEx( IDI_WHITE_14 );
\r
64 icons[nColorUnknown] = LoadIconEx( IDI_UNKNOWN_14 );
\r
65 icons[nClear] = LoadIconEx( IDI_TRANS_14 );
\r
66 icons[nPondering] = LoadIconEx( IDI_PONDER_14 );
\r
67 icons[nThinking] = LoadIconEx( IDI_CLOCK_14 );
\r
68 icons[nAnalyzing] = LoadIconEx( IDI_ANALYZE2_14 );
\r
70 // [HGM] also make a table of handles to output controls
\r
71 // Note that engineOutputDialog must be defined first!
\r
72 outputField[0][nColorIcon] = GetDlgItem( engineOutputDialog, IDC_Color1 );
\r
73 outputField[0][nLabel] = GetDlgItem( engineOutputDialog, IDC_EngineLabel1 );
\r
74 outputField[0][nStateIcon] = GetDlgItem( engineOutputDialog, IDC_StateIcon1 );
\r
75 outputField[0][nStateData] = GetDlgItem( engineOutputDialog, IDC_StateData1 );
\r
76 outputField[0][nLabelNPS] = GetDlgItem( engineOutputDialog, IDC_Engine1_NPS );
\r
77 outputField[0][nMemo] = GetDlgItem( engineOutputDialog, IDC_EngineMemo1 );
\r
79 outputField[1][nColorIcon] = GetDlgItem( engineOutputDialog, IDC_Color2 );
\r
80 outputField[1][nLabel] = GetDlgItem( engineOutputDialog, IDC_EngineLabel2 );
\r
81 outputField[1][nStateIcon] = GetDlgItem( engineOutputDialog, IDC_StateIcon2 );
\r
82 outputField[1][nStateData] = GetDlgItem( engineOutputDialog, IDC_StateData2 );
\r
83 outputField[1][nLabelNPS] = GetDlgItem( engineOutputDialog, IDC_Engine2_NPS );
\r
84 outputField[1][nMemo] = GetDlgItem( engineOutputDialog, IDC_EngineMemo2 );
\r
88 static void SetControlPos( HWND hDlg, int id, int x, int y, int width, int height )
\r
90 HWND hControl = GetDlgItem( hDlg, id );
\r
92 SetWindowPos( hControl, HWND_TOP, x, y, width, height, SWP_NOZORDER );
\r
95 #define HIDDEN_X 20000
\r
96 #define HIDDEN_Y 20000
\r
99 static void HideControl( HWND hDlg, int id )
\r
101 HWND hControl = GetDlgItem( hDlg, id );
\r
104 GetWindowRect( hControl, &rc );
\r
107 Avoid hiding an already hidden control, because that causes many
\r
108 unnecessary WM_ERASEBKGND messages!
\r
110 if( rc.left != HIDDEN_X || rc.top != HIDDEN_Y ) {
\r
111 SetControlPos( hDlg, id, 20000, 20000, 100, 100 );
\r
115 // front end, although we might make GetWindowRect front end instead
\r
116 static int GetControlWidth( HWND hDlg, int id )
\r
120 GetWindowRect( GetDlgItem( hDlg, id ), &rc );
\r
122 return rc.right - rc.left;
\r
126 static int GetControlHeight( HWND hDlg, int id )
\r
130 GetWindowRect( GetDlgItem( hDlg, id ), &rc );
\r
132 return rc.bottom - rc.top;
\r
135 static int GetHeaderHeight()
\r
137 int result = GetControlHeight( engineOutputDialog, IDC_EngineLabel1 );
\r
139 if( result < ICON_SIZE ) result = ICON_SIZE;
\r
144 // The size calculations should be backend? If setControlPos is a platform-dependent way of doing things,
\r
145 // a platform-independent wrapper for it should be supplied.
\r
146 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
148 int label_x = x + ICON_SIZE + H_MARGIN;
\r
149 int label_h = GetControlHeight( hDlg, IDC_EngineLabel1 );
\r
150 int label_y = y + ICON_SIZE - label_h;
\r
151 int nps_w = GetControlWidth( hDlg, IDC_Engine1_NPS );
\r
152 int nps_x = clientWidth - H_MARGIN - nps_w;
\r
153 int state_data_w = GetControlWidth( hDlg, IDC_StateData1 );
\r
154 int state_data_x = nps_x - H_MARGIN - state_data_w;
\r
155 int state_icon_x = state_data_x - ICON_SIZE - 2;
\r
156 int max_w = clientWidth - 2*H_MARGIN;
\r
157 int memo_y = y + ICON_SIZE + LABEL_V_DISTANCE;
\r
159 SetControlPos( hDlg, idColor, x, y, ICON_SIZE, ICON_SIZE );
\r
160 SetControlPos( hDlg, idEngineLabel, label_x, label_y, state_icon_x - label_x, label_h );
\r
161 SetControlPos( hDlg, idStateIcon, state_icon_x, y, ICON_SIZE, ICON_SIZE );
\r
162 SetControlPos( hDlg, idStateData, state_data_x, label_y, state_data_w, label_h );
\r
163 SetControlPos( hDlg, idNPS, nps_x, label_y, nps_w, label_h );
\r
164 SetControlPos( hDlg, idMemo, x, memo_y, max_w, memoHeight );
\r
167 // Also here some of the size calculations should go to the back end, and their actual application to a front-end routine
\r
168 void ResizeWindowControls( int mode )
\r
170 HWND hDlg = engineOutputDialog; // [HGM] used to be parameter, but routine is called from back-end
\r
172 int headerHeight = GetHeaderHeight();
\r
173 // int labelHeight = GetControlHeight( hDlg, IDC_EngineLabel1 );
\r
174 // int labelOffset = H_MARGIN + ICON_SIZE + H_MARGIN;
\r
175 // int labelDeltaY = ICON_SIZE - labelHeight;
\r
178 int maxControlWidth;
\r
181 /* Initialize variables */
\r
182 GetClientRect( hDlg, &rc );
\r
184 clientWidth = rc.right - rc.left;
\r
185 clientHeight = rc.bottom - rc.top;
\r
187 maxControlWidth = clientWidth - 2*H_MARGIN;
\r
189 npsWidth = GetControlWidth( hDlg, IDC_Engine1_NPS );
\r
191 /* Resize controls */
\r
194 PositionControlSet( hDlg, H_MARGIN, V_MARGIN,
\r
196 clientHeight - V_MARGIN - LABEL_V_DISTANCE - headerHeight- V_MARGIN,
\r
197 IDC_Color1, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineMemo1, IDC_StateIcon1, IDC_StateData1 );
\r
199 /* Hide controls for the second engine */
\r
200 HideControl( hDlg, IDC_Color2 );
\r
201 HideControl( hDlg, IDC_EngineLabel2 );
\r
202 HideControl( hDlg, IDC_StateIcon2 );
\r
203 HideControl( hDlg, IDC_StateData2 );
\r
204 HideControl( hDlg, IDC_Engine2_NPS );
\r
205 HideControl( hDlg, IDC_EngineMemo2 );
\r
206 SendDlgItemMessage( hDlg, IDC_EngineMemo2, WM_SETTEXT, 0, (LPARAM) "" );
\r
207 /* TODO: we should also hide/disable them!!! what about tab stops?!?! */
\r
211 int memo_h = (clientHeight - headerHeight*2 - V_MARGIN*2 - LABEL_V_DISTANCE*2 - SPLITTER_SIZE) / 2;
\r
212 int header1_y = V_MARGIN;
\r
213 int header2_y = V_MARGIN + headerHeight + LABEL_V_DISTANCE + memo_h + SPLITTER_SIZE;
\r
215 PositionControlSet( hDlg, H_MARGIN, header1_y, clientWidth, memo_h,
\r
216 IDC_Color1, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineMemo1, IDC_StateIcon1, IDC_StateData1 );
\r
218 PositionControlSet( hDlg, H_MARGIN, header2_y, clientWidth, memo_h,
\r
219 IDC_Color2, IDC_EngineLabel2, IDC_Engine2_NPS, IDC_EngineMemo2, IDC_StateIcon2, IDC_StateData2 );
\r
222 InvalidateRect( GetDlgItem(hDlg,IDC_EngineMemo1), NULL, FALSE );
\r
223 InvalidateRect( GetDlgItem(hDlg,IDC_EngineMemo2), NULL, FALSE );
\r
226 static int currentPV, highTextStart[2], highTextEnd[2];
\r
227 extern RECT boardRect;
\r
230 GetMemoLine(HWND hDlg, int x, int y)
\r
231 { // [HGM] pv: translate click to PV line, and load it for display
\r
233 int index, start, end, memo;
\r
236 pt.x = x; pt.y = y;
\r
237 memo = currentPV ? IDC_EngineMemo2 : IDC_EngineMemo1;
\r
238 index = SendDlgItemMessage( hDlg, memo, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
239 GetDlgItemText(hDlg, memo, buf, sizeof(buf));
\r
240 if(LoadMultiPV(x, y, buf, index, &start, &end)) {
\r
242 SendMessage( outputField[currentPV][nMemo], EM_SETSEL, (WPARAM)start, (LPARAM)end );
\r
243 highTextStart[currentPV] = start; highTextEnd[currentPV] = end;
\r
244 SetFocus(outputField[currentPV][nMemo]);
\r
248 // front end. Actual printing of PV lines into the output field
\r
249 void InsertIntoMemo( int which, char * text, int where )
\r
251 SendMessage( outputField[which][nMemo], EM_SETSEL, where, where ); // [HGM] multivar: choose insertion point
\r
253 SendMessage( outputField[which][nMemo], EM_REPLACESEL, (WPARAM) FALSE, (LPARAM) text );
\r
254 if(where < highTextStart[which]) { // [HGM] multiPVdisplay: move highlighting
\r
255 int len = strlen(text);
\r
256 highTextStart[which] += len; highTextEnd[which] += len;
\r
257 SendMessage( outputField[which][nMemo], EM_SETSEL, highTextStart[which], highTextEnd[which] );
\r
261 // front end. Associates an icon with an output field ("control" in Windows jargon).
\r
262 // [HGM] let it find out the output field from the 'which' number by itself
\r
263 void SetIcon( int which, int field, int nIcon )
\r
267 SendMessage( outputField[which][field], STM_SETICON, (WPARAM) icons[nIcon], 0 );
\r
271 // front end wrapper for SetWindowText, taking control number in stead of handle
\r
272 void DoSetWindowText(int which, int field, char *s_label)
\r
274 SetWindowText( outputField[which][field], s_label );
\r
277 // This seems pure front end
\r
278 LRESULT CALLBACK EngineOutputProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )
\r
280 static SnapData sd;
\r
283 case WM_INITDIALOG:
\r
284 if( engineOutputDialog == NULL ) {
\r
285 engineOutputDialog = hDlg;
\r
287 RestoreWindowPlacement( hDlg, &wpEngineOutput ); /* Restore window placement */
\r
289 ResizeWindowControls( windowMode );
\r
291 SendDlgItemMessage( hDlg, IDC_EngineMemo1, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );
\r
292 SendDlgItemMessage( hDlg, IDC_EngineMemo2, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );
\r
295 SendDlgItemMessage( engineOutputDialog, IDC_EngineMemo1, WM_SETFONT, (WPARAM)font[boardSize][MOVEHISTORY_FONT]->hf, MAKELPARAM(TRUE, 0 ));
\r
296 SendDlgItemMessage( engineOutputDialog, IDC_EngineMemo2, WM_SETFONT, (WPARAM)font[boardSize][MOVEHISTORY_FONT]->hf, MAKELPARAM(TRUE, 0 ));
\r
298 SetEngineState( 0, STATE_IDLE, "" );
\r
299 SetEngineState( 1, STATE_IDLE, "" );
\r
305 switch (LOWORD(wParam)) {
\r
307 EndDialog(hDlg, TRUE);
\r
311 EndDialog(hDlg, FALSE);
\r
321 MovePV(LOWORD(lParam) - boardRect.left, HIWORD(lParam) - boardRect.top, boardRect.bottom - boardRect.top);
\r
326 SendMessage( outputField[currentPV][nMemo], EM_SETSEL, 0, 0 );
\r
327 highTextStart[currentPV] = highTextEnd[currentPV] = 0;
\r
332 if( wParam == IDC_EngineMemo1 || wParam == IDC_EngineMemo2 ) {
\r
333 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
334 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ) {
\r
335 currentPV = (wParam == IDC_EngineMemo2);
\r
336 GetMemoLine(hDlg, LOWORD(lpMF->lParam), HIWORD(lpMF->lParam));
\r
341 case WM_GETMINMAXINFO:
\r
343 MINMAXINFO * mmi = (MINMAXINFO *) lParam;
\r
345 mmi->ptMinTrackSize.x = 100;
\r
346 mmi->ptMinTrackSize.y = 160;
\r
351 EngineOutputPopDown();
\r
355 ResizeWindowControls( windowMode );
\r
358 case WM_ENTERSIZEMOVE:
\r
359 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
362 return OnSizing( &sd, hDlg, wParam, lParam );
\r
365 return OnMoving( &sd, hDlg, wParam, lParam );
\r
367 case WM_EXITSIZEMOVE:
\r
368 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
375 void EngineOutputPopUp()
\r
378 static int needInit = TRUE;
\r
380 CheckMenuItem(GetMenu(hwndMain), IDM_ShowEngineOutput, MF_CHECKED);
\r
382 if( engineOutputDialog ) {
\r
383 SendMessage( engineOutputDialog, WM_INITDIALOG, 0, 0 );
\r
385 if( ! engineOutputDialogUp ) {
\r
386 ShowWindow(engineOutputDialog, SW_SHOW);
\r
390 lpProc = MakeProcInstance( (FARPROC) EngineOutputProc, hInst );
\r
392 /* Note to self: dialog must have the WS_VISIBLE style set, otherwise it's not shown! */
\r
393 CreateDialog( hInst, MAKEINTRESOURCE(DLG_EngineOutput), hwndMain, (DLGPROC)lpProc );
\r
395 FreeProcInstance(lpProc);
\r
398 // [HGM] displaced to after creation of dialog, to allow initialization of output fields
\r
400 InitializeEngineOutput();
\r
404 engineOutputDialogUp = TRUE;
\r
408 void EngineOutputPopDown()
\r
410 CheckMenuItem(GetMenu(hwndMain), IDM_ShowEngineOutput, MF_UNCHECKED);
\r
412 if( engineOutputDialog ) {
\r
413 ShowWindow(engineOutputDialog, SW_HIDE);
\r
416 engineOutputDialogUp = FALSE;
\r
419 // front end. [HGM] Takes handle of output control from table, so only number is passed
\r
420 void DoClearMemo(int which)
\r
422 SendMessage( outputField[which][nMemo], WM_SETTEXT, 0, (LPARAM) "" );
\r
425 // front end (because only other front-end wants to know)
\r
426 int EngineOutputIsUp()
\r
428 return engineOutputDialogUp;
\r
431 // front end, to give back-end access to engineOutputDialog
\r
432 int EngineOutputDialogExists()
\r
434 return engineOutputDialog != NULL;
\r