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