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 void SetEngineOutputTitle(char *title)
\r
279 SetWindowText( engineOutputDialog, title );
\r
282 // This seems pure front end
\r
283 LRESULT CALLBACK EngineOutputProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )
\r
285 static SnapData sd;
\r
288 case WM_INITDIALOG:
\r
289 if( engineOutputDialog == NULL ) {
\r
290 engineOutputDialog = hDlg;
\r
292 Translate(hDlg, DLG_EngineOutput);
\r
293 RestoreWindowPlacement( hDlg, &wpEngineOutput ); /* Restore window placement */
\r
295 ResizeWindowControls( windowMode );
\r
297 SendDlgItemMessage( hDlg, IDC_EngineMemo1, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );
\r
298 SendDlgItemMessage( hDlg, IDC_EngineMemo2, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );
\r
301 SendDlgItemMessage( engineOutputDialog, IDC_EngineMemo1, WM_SETFONT, (WPARAM)font[boardSize][MOVEHISTORY_FONT]->hf, MAKELPARAM(TRUE, 0 ));
\r
302 SendDlgItemMessage( engineOutputDialog, IDC_EngineMemo2, WM_SETFONT, (WPARAM)font[boardSize][MOVEHISTORY_FONT]->hf, MAKELPARAM(TRUE, 0 ));
\r
304 SetEngineState( 0, STATE_IDLE, "" );
\r
305 SetEngineState( 1, STATE_IDLE, "" );
\r
311 switch (LOWORD(wParam)) {
\r
313 EndDialog(hDlg, TRUE);
\r
317 EndDialog(hDlg, FALSE);
\r
327 MovePV(LOWORD(lParam) - boardRect.left, HIWORD(lParam) - boardRect.top, boardRect.bottom - boardRect.top);
\r
332 SendMessage( outputField[currentPV][nMemo], EM_SETSEL, 0, 0 );
\r
333 highTextStart[currentPV] = highTextEnd[currentPV] = 0;
\r
338 if( wParam == IDC_EngineMemo1 || wParam == IDC_EngineMemo2 ) {
\r
339 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
340 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL)) == 0 ) {
\r
341 shiftKey = (lpMF->wParam & MK_SHIFT) != 0; // [HGM] remember last shift status
\r
342 currentPV = (wParam == IDC_EngineMemo2);
\r
343 GetMemoLine(hDlg, LOWORD(lpMF->lParam), HIWORD(lpMF->lParam));
\r
348 case WM_GETMINMAXINFO:
\r
350 MINMAXINFO * mmi = (MINMAXINFO *) lParam;
\r
352 mmi->ptMinTrackSize.x = 100;
\r
353 mmi->ptMinTrackSize.y = 160;
\r
358 EngineOutputPopDown();
\r
362 ResizeWindowControls( windowMode );
\r
365 case WM_ENTERSIZEMOVE:
\r
366 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
369 return OnSizing( &sd, hDlg, wParam, lParam );
\r
372 return OnMoving( &sd, hDlg, wParam, lParam );
\r
374 case WM_EXITSIZEMOVE:
\r
375 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
382 void EngineOutputPopUp()
\r
385 static int needInit = TRUE;
\r
387 CheckMenuItem(GetMenu(hwndMain), IDM_ShowEngineOutput, MF_CHECKED);
\r
389 if( engineOutputDialog ) {
\r
390 SendMessage( engineOutputDialog, WM_INITDIALOG, 0, 0 );
\r
392 if( ! engineOutputDialogUp ) {
\r
393 ShowWindow(engineOutputDialog, SW_SHOW);
\r
397 lpProc = MakeProcInstance( (FARPROC) EngineOutputProc, hInst );
\r
399 /* Note to self: dialog must have the WS_VISIBLE style set, otherwise it's not shown! */
\r
400 CreateDialog( hInst, MAKEINTRESOURCE(DLG_EngineOutput), hwndMain, (DLGPROC)lpProc );
\r
402 FreeProcInstance(lpProc);
\r
405 // [HGM] displaced to after creation of dialog, to allow initialization of output fields
\r
407 InitializeEngineOutput();
\r
411 engineOutputDialogUp = TRUE;
\r
415 void EngineOutputPopDown()
\r
417 CheckMenuItem(GetMenu(hwndMain), IDM_ShowEngineOutput, MF_UNCHECKED);
\r
419 if( engineOutputDialog ) {
\r
420 ShowWindow(engineOutputDialog, SW_HIDE);
\r
423 engineOutputDialogUp = FALSE;
\r
426 // front end. [HGM] Takes handle of output control from table, so only number is passed
\r
427 void DoClearMemo(int which)
\r
429 SendMessage( outputField[which][nMemo], WM_SETTEXT, 0, (LPARAM) "" );
\r
432 // front end (because only other front-end wants to know)
\r
433 int EngineOutputIsUp()
\r
435 return engineOutputDialogUp;
\r
438 // front end, to give back-end access to engineOutputDialog
\r
439 int EngineOutputDialogExists()
\r
441 return engineOutputDialog != NULL;
\r