Fix multi-leg promotions
[xboard.git] / winboard / whistory.c
1 /*\r
2  * Move history for WinBoard\r
3  *\r
4  * Author: Alessandro Scotti (Dec 2005)\r
5  * front-end code split off by HGM\r
6  *\r
7  * Copyright 2005 Alessandro Scotti\r
8  *\r
9  * Enhancements Copyright 2009, 2010, 2014, 2015, 2016 Free Software\r
10  * Foundation, Inc.\r
11  *\r
12  * ------------------------------------------------------------------------\r
13  *\r
14  * GNU XBoard is free software: you can redistribute it and/or modify\r
15  * it under the terms of the GNU General Public License as published by\r
16  * the Free Software Foundation, either version 3 of the License, or (at\r
17  * your option) any later version.\r
18  *\r
19  * GNU XBoard is distributed in the hope that it will be useful, but\r
20  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
22  * General Public License for more details.\r
23  *\r
24  * You should have received a copy of the GNU General Public License\r
25  * along with this program. If not, see http://www.gnu.org/licenses/.\r
26  *\r
27  * ------------------------------------------------------------------------\r
28  ** See the file ChangeLog for a revision history.  */\r
29 \r
30 #include "config.h"\r
31 \r
32 #include <stdio.h>\r
33 #include <stdlib.h>\r
34 #include <malloc.h>\r
35 #include <windows.h> /* required for all Windows applications */\r
36 #include <richedit.h>\r
37 #include <commdlg.h>\r
38 #include <dlgs.h>\r
39 \r
40 #include "common.h"\r
41 #include "frontend.h"\r
42 #include "backend.h"\r
43 #include "winboard.h"\r
44 #include "wsnap.h"\r
45 \r
46 // templates for calls into back-end\r
47 void RefreshMemoContent P((void));\r
48 void MemoContentUpdated P((void));\r
49 void FindMoveByCharIndex P(( int char_index ));\r
50 \r
51 #define DEFAULT_COLOR       0xFFFFFFFF\r
52 \r
53 #define H_MARGIN            2\r
54 #define V_MARGIN            2\r
55 \r
56 static BOOLEAN moveHistoryDialogUp = FALSE;\r
57 \r
58 // ------------- low-level front-end actions called by MoveHistory back-end -----------------\r
59 \r
60 // low-level front-end, after calculating from & to is left to caller\r
61 // it task is to highlight the indicated characters. (In WinBoard it makes them bold and blue.)\r
62 void HighlightMove( int from, int to, Boolean highlight )\r
63 {\r
64         CHARFORMAT cf;\r
65         HWND hMemo = GetDlgItem( moveHistoryDialog, IDC_MoveHistory );\r
66 \r
67         SendMessage( hMemo, EM_SETSEL, from, to);\r
68 \r
69 \r
70         /* Set style */\r
71         ZeroMemory( &cf, sizeof(cf) );\r
72 \r
73         cf.cbSize = sizeof(cf);\r
74         cf.dwMask = CFM_BOLD | CFM_COLOR;\r
75 \r
76         if( highlight ) {\r
77             cf.dwEffects |= CFE_BOLD;\r
78             cf.crTextColor = RGB( 0x00, 0x00, 0xFF );\r
79         }\r
80         else {\r
81             cf.dwEffects |= CFE_AUTOCOLOR;\r
82         }\r
83 \r
84         SendMessage( hMemo, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf );\r
85 }\r
86 \r
87 // low-level front-end, but replace Windows data types to make it callable from back-end\r
88 // its task is to clear the contents of the move-history text edit\r
89 void ClearHistoryMemo()\r
90 {\r
91     SendDlgItemMessage( moveHistoryDialog, IDC_MoveHistory, WM_SETTEXT, 0, (LPARAM) "" );\r
92 }\r
93 \r
94 // low-level front-end, made callable from back-end by passing flags and color numbers\r
95 // its task is to append the given text to the text edit\r
96 // the bold argument says 0 = normal, 1 = bold typeface\r
97 // the colorNr argument says 0 = font-default, 1 = gray\r
98 int AppendToHistoryMemo( char * text, int bold, int colorNr )\r
99 {\r
100     CHARFORMAT cf;\r
101     DWORD flags = bold ? CFE_BOLD :0;\r
102     DWORD color = colorNr ? GetSysColor(COLOR_GRAYTEXT) : DEFAULT_COLOR;\r
103 \r
104     HWND hMemo = GetDlgItem( moveHistoryDialog, IDC_MoveHistory );\r
105 \r
106     /* Select end of text */\r
107     int cbTextLen = (int) SendMessage( hMemo, WM_GETTEXTLENGTH, 0, 0 );\r
108 \r
109     SendMessage( hMemo, EM_SETSEL, cbTextLen, cbTextLen );\r
110 \r
111     /* Set style */\r
112     ZeroMemory( &cf, sizeof(cf) );\r
113 \r
114     cf.cbSize = sizeof(cf);\r
115     cf.dwMask = CFM_BOLD | CFM_ITALIC | CFM_COLOR | CFM_UNDERLINE;\r
116     cf.dwEffects = flags;\r
117 \r
118     if( color != DEFAULT_COLOR ) {\r
119         cf.crTextColor = color;\r
120     }\r
121     else {\r
122         cf.dwEffects |= CFE_AUTOCOLOR;\r
123     }\r
124 \r
125     SendMessage( hMemo, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf );\r
126 \r
127     /* Append text */\r
128     SendMessage( hMemo, EM_REPLACESEL, (WPARAM) FALSE, (LPARAM) text );\r
129 \r
130     /* Return offset of appended text */\r
131     return cbTextLen;\r
132 }\r
133 \r
134 // low-level front-end; wrapper for the code to scroll the mentioned character in view (-1 = end)\r
135 void ScrollToCurrent(int caretPos)\r
136 {\r
137     if(caretPos < 0)\r
138         caretPos = (int) SendDlgItemMessage( moveHistoryDialog, IDC_MoveHistory, WM_GETTEXTLENGTH, 0, 0 );\r
139     SendDlgItemMessage( moveHistoryDialog, IDC_MoveHistory, EM_SETSEL, caretPos, caretPos );\r
140 \r
141     SendDlgItemMessage( moveHistoryDialog, IDC_MoveHistory, EM_SCROLLCARET, 0, 0 );\r
142 }\r
143 \r
144 \r
145 // ------------------------------ call backs --------------------------\r
146 \r
147 // front-end. Universal call-back for any event. Recognized vents are dialog creation, OK and cancel button-press\r
148 // (dead code, as these buttons do not exist?), mouse clicks on the text edit, and moving / sizing\r
149 LRESULT CALLBACK HistoryDialogProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )\r
150 {\r
151     static SnapData sd;\r
152 \r
153     switch (message) {\r
154     case WM_INITDIALOG:\r
155         if( moveHistoryDialog == NULL ) {\r
156             moveHistoryDialog = hDlg;\r
157             Translate(hDlg, DLG_MoveHistory);\r
158 \r
159             /* Enable word wrapping and notifications */\r
160             SendDlgItemMessage( moveHistoryDialog, IDC_MoveHistory, EM_SETTARGETDEVICE, 0, 0 );\r
161 \r
162             SendDlgItemMessage( moveHistoryDialog, IDC_MoveHistory, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );\r
163 \r
164             /* Set font */\r
165             SendDlgItemMessage( moveHistoryDialog, IDC_MoveHistory, WM_SETFONT, (WPARAM)font[boardSize][MOVEHISTORY_FONT]->hf, MAKELPARAM(TRUE, 0 ));\r
166 \r
167             /* Restore window placement */\r
168             RestoreWindowPlacement( hDlg, &wpMoveHistory );\r
169         }\r
170 \r
171         /* Update memo */\r
172         RefreshMemoContent();\r
173 \r
174         MemoContentUpdated();\r
175 \r
176         return FALSE;\r
177 \r
178     case WM_COMMAND:\r
179         switch (LOWORD(wParam)) {\r
180         case IDOK:\r
181           EndDialog(hDlg, TRUE);\r
182           return TRUE;\r
183 \r
184         case IDCANCEL:\r
185           EndDialog(hDlg, FALSE);\r
186           return TRUE;\r
187 \r
188         default:\r
189           break;\r
190         }\r
191 \r
192         break;\r
193 \r
194     case WM_NOTIFY:\r
195         if( wParam == IDC_MoveHistory ) {\r
196             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
197 \r
198             if( lpMF->msg == WM_LBUTTONDBLCLK && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ) {\r
199                 POINTL pt;\r
200                 LRESULT index;\r
201 \r
202                 pt.x = LOWORD( lpMF->lParam );\r
203                 pt.y = HIWORD( lpMF->lParam );\r
204 \r
205                 index = SendDlgItemMessage( hDlg, IDC_MoveHistory, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
206 \r
207                 FindMoveByCharIndex( index ); // [HGM] also does the actual moving to it, now\r
208 \r
209                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
210                 lpMF->msg = WM_USER;\r
211 \r
212                 return TRUE;\r
213             }\r
214         }\r
215         break;\r
216 \r
217     case WM_SIZE:\r
218         SetWindowPos( GetDlgItem( moveHistoryDialog, IDC_MoveHistory ),\r
219             HWND_TOP,\r
220             H_MARGIN, V_MARGIN,\r
221             LOWORD(lParam) - 2*H_MARGIN,\r
222             HIWORD(lParam) - 2*V_MARGIN,\r
223             SWP_NOZORDER );\r
224         break;\r
225 \r
226     case WM_GETMINMAXINFO:\r
227         {\r
228             MINMAXINFO * mmi = (MINMAXINFO *) lParam;\r
229         \r
230             mmi->ptMinTrackSize.x = 100;\r
231             mmi->ptMinTrackSize.y = 100;\r
232         }\r
233         break;\r
234 \r
235     case WM_CLOSE:\r
236         MoveHistoryPopDown();\r
237         break;\r
238 \r
239     case WM_ENTERSIZEMOVE:\r
240         return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
241 \r
242     case WM_SIZING:\r
243         return OnSizing( &sd, hDlg, wParam, lParam );\r
244 \r
245     case WM_MOVING:\r
246         return OnMoving( &sd, hDlg, wParam, lParam );\r
247 \r
248     case WM_EXITSIZEMOVE:\r
249         return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
250     }\r
251 \r
252     return FALSE;\r
253 }\r
254 \r
255 // ------------ standard entry points into MoveHistory code -----------\r
256 \r
257 // front-end\r
258 VOID MoveHistoryPopUp()\r
259 {\r
260   FARPROC lpProc;\r
261   \r
262   CheckMenuItem(GetMenu(hwndMain), IDM_ShowMoveHistory, MF_CHECKED);\r
263 \r
264   if( moveHistoryDialog ) {\r
265     SendMessage( moveHistoryDialog, WM_INITDIALOG, 0, 0 );\r
266 \r
267     if( ! moveHistoryDialogUp ) {\r
268         ShowWindow(moveHistoryDialog, SW_SHOW);\r
269     }\r
270   }\r
271   else {\r
272     lpProc = MakeProcInstance( (FARPROC) HistoryDialogProc, hInst );\r
273 \r
274     /* Note to self: dialog must have the WS_VISIBLE style set, otherwise it's not shown! */\r
275     CreateDialog( hInst, MAKEINTRESOURCE(DLG_MoveHistory), hwndMain, (DLGPROC)lpProc );\r
276 \r
277     FreeProcInstance(lpProc);\r
278   }\r
279 \r
280   moveHistoryDialogUp = TRUE;\r
281 \r
282 // Note that in WIndows creating the dialog causes its call-back to perform\r
283 // RefreshMemoContent() and MemoContentUpdated() immediately after it is realized.\r
284 // To port this to X we might have to do that from here.\r
285 }\r
286 \r
287 // front-end\r
288 VOID MoveHistoryPopDown()\r
289 {\r
290   CheckMenuItem(GetMenu(hwndMain), IDM_ShowMoveHistory, MF_UNCHECKED);\r
291 \r
292   if( moveHistoryDialog ) {\r
293       ShowWindow(moveHistoryDialog, SW_HIDE);\r
294   }\r
295 \r
296   moveHistoryDialogUp = FALSE;\r
297 }\r
298 \r
299 // front-end\r
300 Boolean MoveHistoryIsUp()\r
301 {\r
302     return moveHistoryDialogUp;\r
303 }\r
304 \r
305 // front-end\r
306 Boolean MoveHistoryDialogExists()\r
307 {\r
308     return moveHistoryDialog != NULL;\r
309 }\r