85171480b1a5ffc4a6b48c71a1bc70c22c115f38
[reactos.git] / reactos / dll / cpl / console / options.c
1 /*
2 * PROJECT: ReactOS Console Configuration DLL
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: dll/cpl/console/options.c
5 * PURPOSE: Options dialog
6 * PROGRAMMERS: Johannes Anderwald (johannes.anderwald@reactos.org)
7 * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
8 */
9
10 #include "console.h"
11
12 #define NDEBUG
13 #include <debug.h>
14
15 #define MAX_VALUE_NAME 16383
16
17
18 /*
19 * A function that locates the insertion point (index) for a given value 'Value'
20 * in a list 'List' to maintain its sorted order by increasing values.
21 *
22 * - When 'BisectRightOrLeft' == TRUE, the bisection is performed to the right,
23 * i.e. the returned insertion point comes after (to the right of) any existing
24 * entries of 'Value' in 'List'.
25 * The returned insertion point 'i' partitions the list 'List' into two halves
26 * such that:
27 * all(val <= Value for val in List[start:i[) for the left side, and
28 * all(val > Value for val in List[i:end+1[) for the right side.
29 *
30 * - When 'BisectRightOrLeft' == FALSE, the bisection is performed to the left,
31 * i.e. the returned insertion point comes before (to the left of) any existing
32 * entries of 'Value' in 'List'.
33 * The returned insertion point 'i' partitions the list 'List' into two halves
34 * such that:
35 * all(val < Value for val in List[start:i[) for the left side, and
36 * all(val >= Value for val in List[i:end+1[) for the right side.
37 *
38 * The exact value of List[i] may, or may not, be equal to Value, depending on
39 * whether or not 'Value' is actually present on the list.
40 */
41 static UINT
42 BisectListSortedByValueEx(
43 IN HWND hWndList,
44 IN ULONG_PTR Value,
45 IN UINT itemStart,
46 IN UINT itemEnd,
47 OUT PUINT pValueItem OPTIONAL,
48 IN BOOL BisectRightOrLeft)
49 {
50 UINT iItemStart, iItemEnd, iItem;
51 ULONG_PTR itemData;
52
53 /* Sanity checks */
54 if (itemStart > itemEnd)
55 return CB_ERR; // Fail
56
57 /* Initialize */
58 iItemStart = itemStart;
59 iItemEnd = itemEnd;
60 iItem = iItemStart;
61
62 if (pValueItem)
63 *pValueItem = CB_ERR;
64
65 while (iItemStart <= iItemEnd)
66 {
67 /*
68 * Bisect. Note the following:
69 * - if iItemEnd == iItemStart + 1, then iItem == iItemStart;
70 * - if iItemStart == iItemEnd, then iItemStart == iItem == iItemEnd.
71 * In all but the last case, iItemStart <= iItem < iItemEnd.
72 */
73 iItem = (iItemStart + iItemEnd) / 2;
74
75 itemData = (ULONG_PTR)SendMessageW(hWndList, CB_GETITEMDATA, (WPARAM)iItem, 0);
76 if (itemData == CB_ERR)
77 return CB_ERR; // Fail
78
79 if (Value == itemData)
80 {
81 /* Found a candidate */
82 if (pValueItem)
83 *pValueItem = iItem;
84
85 /*
86 * Try to find the last element (if BisectRightOrLeft == TRUE)
87 * or the first element (if BisectRightOrLeft == FALSE).
88 */
89 if (BisectRightOrLeft)
90 {
91 iItemStart = iItem + 1; // iItemStart may be > iItemEnd
92 }
93 else
94 {
95 if (iItem <= itemStart) break;
96 iItemEnd = iItem - 1; // iItemEnd may be < iItemStart, i.e. iItemStart may be > iItemEnd
97 }
98 }
99 else if (Value < itemData)
100 {
101 if (iItem <= itemStart) break;
102 /* The value should be before iItem */
103 iItemEnd = iItem - 1; // iItemEnd may be < iItemStart, i.e. iItemStart may be > iItemEnd, if iItem == iItemStart.
104 }
105 else // if (itemData < Value)
106 {
107 /* The value should be after iItem */
108 iItemStart = iItem + 1; // iItemStart may be > iItemEnd, if iItem == iItemEnd.
109 }
110
111 /* Here, iItemStart may be == iItemEnd */
112 }
113
114 return iItemStart;
115 }
116
117 static UINT
118 BisectListSortedByValue(
119 IN HWND hWndList,
120 IN ULONG_PTR Value,
121 OUT PUINT pValueItem OPTIONAL,
122 IN BOOL BisectRightOrLeft)
123 {
124 INT iItemEnd = (INT)SendMessageW(hWndList, CB_GETCOUNT, 0, 0);
125 if (iItemEnd == CB_ERR || iItemEnd <= 0)
126 return CB_ERR; // Fail
127
128 return BisectListSortedByValueEx(hWndList, Value,
129 0, (UINT)(iItemEnd - 1),
130 pValueItem,
131 BisectRightOrLeft);
132 }
133
134
135 static VOID
136 AddCodePage(
137 IN HWND hWndList,
138 IN UINT CodePage)
139 {
140 UINT iItem, iDupItem;
141 CPINFOEXW CPInfo;
142
143 /*
144 * Add only valid code pages, that is:
145 * - If the CodePage is one of the reserved (alias) values:
146 * CP_ACP == 0 ; CP_OEMCP == 1 ; CP_MACCP == 2 ; CP_THREAD_ACP == 3 ;
147 * or the deprecated CP_SYMBOL == 42 (see http://archives.miloush.net/michkap/archive/2005/11/08/490495.html)
148 * it is considered invalid.
149 * - If IsValidCodePage() fails because the code page is listed but
150 * not installed on the system, it is also considered invalid.
151 */
152 if (CodePage == CP_ACP || CodePage == CP_OEMCP || CodePage == CP_MACCP ||
153 CodePage == CP_THREAD_ACP || CodePage == CP_SYMBOL || !IsValidCodePage(CodePage))
154 {
155 return;
156 }
157
158 /* Retrieve the code page display name */
159 if (!GetCPInfoExW(CodePage, 0, &CPInfo))
160 {
161 /* We failed, just use the code page value as its name */
162 // _ultow(CodePage, CPInfo.CodePageName, 10);
163 _snwprintf(CPInfo.CodePageName, ARRAYSIZE(CPInfo.CodePageName), L"%lu", CodePage);
164 }
165
166 /* Add the code page into the list, sorted by code page value. Avoid any duplicates. */
167 iDupItem = CB_ERR;
168 iItem = BisectListSortedByValue(hWndList, CodePage, &iDupItem, TRUE);
169 if (iItem == CB_ERR)
170 iItem = 0;
171 if (iDupItem != CB_ERR)
172 return;
173 iItem = (UINT)SendMessageW(hWndList, CB_INSERTSTRING, iItem, (LPARAM)CPInfo.CodePageName);
174 if (iItem != CB_ERR && iItem != CB_ERRSPACE)
175 iItem = SendMessageW(hWndList, CB_SETITEMDATA, iItem, CodePage);
176 }
177
178 static VOID
179 BuildCodePageList(IN HWND hDlg)
180 {
181 HWND hWndList;
182 HKEY hKey;
183 DWORD dwIndex, dwSize, dwType;
184 UINT CodePage;
185 WCHAR szValueName[MAX_VALUE_NAME];
186
187 // #define REGSTR_PATH_CODEPAGE TEXT("System\\CurrentControlSet\\Control\\Nls\\CodePage")
188 /* Open the Nls\CodePage key */
189 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
190 L"System\\CurrentControlSet\\Control\\Nls\\CodePage",
191 0,
192 KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE,
193 &hKey) != ERROR_SUCCESS)
194 {
195 return;
196 }
197
198 hWndList = GetDlgItem(hDlg, IDL_CODEPAGE);
199
200 /* Enumerate all the available code pages on the system */
201 dwSize = ARRAYSIZE(szValueName);
202 dwIndex = 0;
203 while (RegEnumValueW(hKey, dwIndex, szValueName, &dwSize,
204 NULL, &dwType, NULL, NULL) == ERROR_SUCCESS) // != ERROR_NO_MORE_ITEMS
205 {
206 /* Ignore these parameters, prepare for next iteration */
207 dwSize = ARRAYSIZE(szValueName);
208 ++dwIndex;
209
210 /* Check the value type validity */
211 if (dwType != REG_SZ)
212 continue;
213
214 /*
215 * Add the code page into the list.
216 * If _wtol fails and returns 0, the code page is considered invalid
217 * (and indeed this value corresponds to the CP_ACP alias too).
218 */
219 CodePage = (UINT)_wtol(szValueName);
220 if (CodePage == 0) continue;
221 AddCodePage(hWndList, CodePage);
222 }
223
224 RegCloseKey(hKey);
225
226 /* Add the special UTF-7 (CP_UTF7 65000) and UTF-8 (CP_UTF8 65001) code pages */
227 AddCodePage(hWndList, CP_UTF7);
228 AddCodePage(hWndList, CP_UTF8);
229
230 /* Find and select the current code page in the sorted list */
231 if (BisectListSortedByValue(hWndList, ConInfo->CodePage, &CodePage, FALSE) == CB_ERR ||
232 CodePage == CB_ERR)
233 {
234 /* Not found, select the first element */
235 CodePage = 0;
236 }
237 SendMessageW(hWndList, CB_SETCURSEL, (WPARAM)CodePage, 0);
238 }
239
240 static VOID
241 UpdateDialogElements(HWND hwndDlg, PCONSOLE_STATE_INFO pConInfo)
242 {
243 HWND hDlgCtrl;
244
245 /* Update cursor size */
246 if (pConInfo->CursorSize <= 25)
247 {
248 /* Small cursor */
249 hDlgCtrl = GetDlgItem(hwndDlg, IDC_RADIO_SMALL_CURSOR);
250 SendMessageW(hDlgCtrl, BM_SETCHECK, (WPARAM)BST_CHECKED, 0);
251
252 hDlgCtrl = GetDlgItem(hwndDlg, IDC_RADIO_MEDIUM_CURSOR);
253 SendMessageW(hDlgCtrl, BM_SETCHECK, (WPARAM)BST_UNCHECKED, 0);
254 hDlgCtrl = GetDlgItem(hwndDlg, IDC_RADIO_LARGE_CURSOR);
255 SendMessageW(hDlgCtrl, BM_SETCHECK, (WPARAM)BST_UNCHECKED, 0);
256 }
257 else if (pConInfo->CursorSize <= 50)
258 {
259 hDlgCtrl = GetDlgItem(hwndDlg, IDC_RADIO_MEDIUM_CURSOR);
260 SendMessageW(hDlgCtrl, BM_SETCHECK, (WPARAM)BST_CHECKED, 0);
261
262 hDlgCtrl = GetDlgItem(hwndDlg, IDC_RADIO_SMALL_CURSOR);
263 SendMessageW(hDlgCtrl, BM_SETCHECK, (WPARAM)BST_UNCHECKED, 0);
264 hDlgCtrl = GetDlgItem(hwndDlg, IDC_RADIO_LARGE_CURSOR);
265 SendMessageW(hDlgCtrl, BM_SETCHECK, (WPARAM)BST_UNCHECKED, 0);
266 }
267 else /* if (pConInfo->CursorSize <= 100) */
268 {
269 hDlgCtrl = GetDlgItem(hwndDlg, IDC_RADIO_LARGE_CURSOR);
270 SendMessageW(hDlgCtrl, BM_SETCHECK, (WPARAM)BST_CHECKED, 0);
271
272 hDlgCtrl = GetDlgItem(hwndDlg, IDC_RADIO_SMALL_CURSOR);
273 SendMessageW(hDlgCtrl, BM_SETCHECK, (WPARAM)BST_UNCHECKED, 0);
274 hDlgCtrl = GetDlgItem(hwndDlg, IDC_RADIO_MEDIUM_CURSOR);
275 SendMessageW(hDlgCtrl, BM_SETCHECK, (WPARAM)BST_UNCHECKED, 0);
276 }
277
278 /* Update num buffers */
279 hDlgCtrl = GetDlgItem(hwndDlg, IDC_UPDOWN_NUM_BUFFER);
280 SendMessageW(hDlgCtrl, UDM_SETRANGE, 0, MAKELONG(999, 1));
281 SetDlgItemInt(hwndDlg, IDC_EDIT_NUM_BUFFER, pConInfo->NumberOfHistoryBuffers, FALSE);
282
283 /* Update buffer size */
284 hDlgCtrl = GetDlgItem(hwndDlg, IDC_UPDOWN_BUFFER_SIZE);
285 SendMessageW(hDlgCtrl, UDM_SETRANGE, 0, MAKELONG(999, 1));
286 SetDlgItemInt(hwndDlg, IDC_EDIT_BUFFER_SIZE, pConInfo->HistoryBufferSize, FALSE);
287
288 /* Update discard duplicates */
289 CheckDlgButton(hwndDlg, IDC_CHECK_DISCARD_DUPLICATES,
290 pConInfo->HistoryNoDup ? BST_CHECKED : BST_UNCHECKED);
291
292 /* Update full/window screen */
293 if (pConInfo->FullScreen)
294 {
295 hDlgCtrl = GetDlgItem(hwndDlg, IDC_RADIO_DISPLAY_FULL);
296 SendMessageW(hDlgCtrl, BM_SETCHECK, (WPARAM)BST_CHECKED, 0);
297
298 hDlgCtrl = GetDlgItem(hwndDlg, IDC_RADIO_DISPLAY_WINDOW);
299 SendMessageW(hDlgCtrl, BM_SETCHECK, (WPARAM)BST_UNCHECKED, 0);
300 }
301 else
302 {
303 hDlgCtrl = GetDlgItem(hwndDlg, IDC_RADIO_DISPLAY_WINDOW);
304 SendMessageW(hDlgCtrl, BM_SETCHECK, (WPARAM)BST_CHECKED, 0);
305
306 hDlgCtrl = GetDlgItem(hwndDlg, IDC_RADIO_DISPLAY_FULL);
307 SendMessageW(hDlgCtrl, BM_SETCHECK, (WPARAM)BST_UNCHECKED, 0);
308 }
309
310 /* Update quick edit */
311 CheckDlgButton(hwndDlg, IDC_CHECK_QUICK_EDIT,
312 pConInfo->QuickEdit ? BST_CHECKED : BST_UNCHECKED);
313
314 /* Update insert mode */
315 CheckDlgButton(hwndDlg, IDC_CHECK_INSERT_MODE,
316 pConInfo->InsertMode ? BST_CHECKED : BST_UNCHECKED);
317 }
318
319 INT_PTR
320 CALLBACK
321 OptionsProc(HWND hwndDlg,
322 UINT uMsg,
323 WPARAM wParam,
324 LPARAM lParam)
325 {
326 switch (uMsg)
327 {
328 case WM_INITDIALOG:
329 {
330 BuildCodePageList(hwndDlg);
331 UpdateDialogElements(hwndDlg, ConInfo);
332 return TRUE;
333 }
334
335 case WM_NOTIFY:
336 {
337 LPPSHNOTIFY lppsn = (LPPSHNOTIFY)lParam;
338
339 if (lppsn->hdr.code == UDN_DELTAPOS)
340 {
341 LPNMUPDOWN lpnmud = (LPNMUPDOWN)lParam;
342
343 if (lppsn->hdr.idFrom == IDC_UPDOWN_BUFFER_SIZE)
344 {
345 lpnmud->iPos = min(max(lpnmud->iPos + lpnmud->iDelta, 1), 999);
346 ConInfo->HistoryBufferSize = lpnmud->iPos;
347 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
348 }
349 else if (lppsn->hdr.idFrom == IDC_UPDOWN_NUM_BUFFER)
350 {
351 lpnmud->iPos = min(max(lpnmud->iPos + lpnmud->iDelta, 1), 999);
352 ConInfo->NumberOfHistoryBuffers = lpnmud->iPos;
353 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
354 }
355 }
356 else if (lppsn->hdr.code == PSN_APPLY)
357 {
358 ApplyConsoleInfo(hwndDlg);
359 return TRUE;
360 }
361 break;
362 }
363
364 case WM_COMMAND:
365 {
366 LRESULT lResult;
367
368 switch (LOWORD(wParam))
369 {
370 case IDC_RADIO_SMALL_CURSOR:
371 {
372 ConInfo->CursorSize = 25;
373 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
374 break;
375 }
376 case IDC_RADIO_MEDIUM_CURSOR:
377 {
378 ConInfo->CursorSize = 50;
379 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
380 break;
381 }
382 case IDC_RADIO_LARGE_CURSOR:
383 {
384 ConInfo->CursorSize = 100;
385 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
386 break;
387 }
388 case IDC_RADIO_DISPLAY_WINDOW:
389 {
390 ConInfo->FullScreen = FALSE;
391 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
392 break;
393 }
394 case IDC_RADIO_DISPLAY_FULL:
395 {
396 ConInfo->FullScreen = TRUE;
397 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
398 break;
399 }
400 case IDC_CHECK_QUICK_EDIT:
401 {
402 lResult = SendMessageW((HWND)lParam, BM_GETCHECK, 0, 0);
403 if (lResult == BST_CHECKED)
404 {
405 ConInfo->QuickEdit = FALSE;
406 SendMessageW((HWND)lParam, BM_SETCHECK, (WPARAM)BST_UNCHECKED, 0);
407 }
408 else if (lResult == BST_UNCHECKED)
409 {
410 ConInfo->QuickEdit = TRUE;
411 SendMessageW((HWND)lParam, BM_SETCHECK, (WPARAM)BST_CHECKED, 0);
412 }
413 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
414 break;
415 }
416 case IDC_CHECK_INSERT_MODE:
417 {
418 lResult = SendMessageW((HWND)lParam, BM_GETCHECK, 0, 0);
419 if (lResult == BST_CHECKED)
420 {
421 ConInfo->InsertMode = FALSE;
422 SendMessageW((HWND)lParam, BM_SETCHECK, (WPARAM)BST_UNCHECKED, 0);
423 }
424 else if (lResult == BST_UNCHECKED)
425 {
426 ConInfo->InsertMode = TRUE;
427 SendMessageW((HWND)lParam, BM_SETCHECK, (WPARAM)BST_CHECKED, 0);
428 }
429 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
430 break;
431 }
432 case IDC_CHECK_DISCARD_DUPLICATES:
433 {
434 lResult = SendMessageW((HWND)lParam, BM_GETCHECK, 0, 0);
435 if (lResult == BST_CHECKED)
436 {
437 ConInfo->HistoryNoDup = FALSE;
438 SendMessageW((HWND)lParam, BM_SETCHECK, (WPARAM)BST_UNCHECKED, 0);
439 }
440 else if (lResult == BST_UNCHECKED)
441 {
442 ConInfo->HistoryNoDup = TRUE;
443 SendMessageW((HWND)lParam, BM_SETCHECK, (WPARAM)BST_CHECKED, 0);
444 }
445 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
446 break;
447 }
448 case IDC_EDIT_BUFFER_SIZE:
449 {
450 if (HIWORD(wParam) == EN_KILLFOCUS)
451 {
452 DWORD sizeBuff;
453
454 sizeBuff = GetDlgItemInt(hwndDlg, IDC_EDIT_BUFFER_SIZE, NULL, FALSE);
455 sizeBuff = min(max(sizeBuff, 1), 999);
456
457 ConInfo->HistoryBufferSize = sizeBuff;
458 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
459 }
460 break;
461 }
462 case IDC_EDIT_NUM_BUFFER:
463 {
464 if (HIWORD(wParam) == EN_KILLFOCUS)
465 {
466 DWORD numBuff;
467
468 numBuff = GetDlgItemInt(hwndDlg, IDC_EDIT_NUM_BUFFER, NULL, FALSE);
469 numBuff = min(max(numBuff, 1), 999);
470
471 ConInfo->NumberOfHistoryBuffers = numBuff;
472 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
473 }
474 break;
475 }
476 case IDL_CODEPAGE:
477 {
478 if (HIWORD(wParam) == CBN_SELENDOK)
479 {
480 INT iItem;
481 UINT CodePage;
482
483 iItem = (INT)SendMessageW((HWND)lParam, CB_GETCURSEL, 0, 0);
484 if (iItem != CB_ERR)
485 {
486 CodePage = (UINT)SendMessageW((HWND)lParam, CB_GETITEMDATA, iItem, 0);
487 if (CodePage != CB_ERR)
488 {
489 ConInfo->CodePage = CodePage;
490 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
491 }
492 }
493 }
494 break;
495 }
496 default:
497 break;
498 }
499 break;
500 }
501
502 default:
503 break;
504 }
505
506 return FALSE;
507 }