xref: /reactos/dll/cpl/console/options.c (revision 3c5a56ed)
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 static INT
19 List_GetCount(IN PLIST_CTL ListCtl)
20 {
21     return (INT)SendMessageW(ListCtl->hWndList, CB_GETCOUNT, 0, 0);
22 }
23 
24 static ULONG_PTR
25 List_GetData(IN PLIST_CTL ListCtl, IN INT Index)
26 {
27     return (ULONG_PTR)SendMessageW(ListCtl->hWndList, CB_GETITEMDATA, (WPARAM)Index, 0);
28 }
29 
30 static VOID
31 AddCodePage(
32     IN PLIST_CTL ListCtl,
33     IN UINT CodePage)
34 {
35     UINT iItem, iDupItem;
36     CPINFOEXW CPInfo;
37 
38     /*
39      * Add only valid code pages, that is:
40      * - If the CodePage is one of the reserved (alias) values:
41      *   CP_ACP == 0 ; CP_OEMCP == 1 ; CP_MACCP == 2 ; CP_THREAD_ACP == 3 ;
42      *   or the deprecated CP_SYMBOL == 42 (see http://archives.miloush.net/michkap/archive/2005/11/08/490495.html)
43      *   it is considered invalid.
44      * - If IsValidCodePage() fails because the code page is listed but
45      *   not installed on the system, it is also considered invalid.
46      */
47     if (CodePage == CP_ACP || CodePage == CP_OEMCP || CodePage == CP_MACCP ||
48         CodePage == CP_THREAD_ACP || CodePage == CP_SYMBOL || !IsValidCodePage(CodePage))
49     {
50         return;
51     }
52 
53     /* Retrieve the code page display name */
54     if (!GetCPInfoExW(CodePage, 0, &CPInfo))
55     {
56         /* We failed, just use the code page value as its name */
57         // _ultow(CodePage, CPInfo.CodePageName, 10);
58         StringCchPrintfW(CPInfo.CodePageName, ARRAYSIZE(CPInfo.CodePageName), L"%lu", CodePage);
59     }
60 
61     /* Add the code page into the list, sorted by code page value. Avoid any duplicates. */
62     iDupItem = CB_ERR;
63     iItem = BisectListSortedByValue(ListCtl, CodePage, &iDupItem, TRUE);
64     if (iItem == CB_ERR)
65         iItem = 0;
66     if (iDupItem != CB_ERR)
67         return;
68     iItem = (UINT)SendMessageW(ListCtl->hWndList, CB_INSERTSTRING, iItem, (LPARAM)CPInfo.CodePageName);
69     if (iItem != CB_ERR && iItem != CB_ERRSPACE)
70         iItem = SendMessageW(ListCtl->hWndList, CB_SETITEMDATA, iItem, CodePage);
71 }
72 
73 static VOID
74 BuildCodePageList(
75     IN HWND hDlg,
76     IN UINT CurrentCodePage)
77 {
78     LIST_CTL ListCtl;
79     LRESULT lResult;
80     HKEY hKey;
81     DWORD dwIndex, dwType;
82     DWORD cchValueName;
83     UINT CodePage;
84 
85     /* Valid code page value names are string representations
86      * of their corresponding decimal values, that are not larger
87      * than MAXUSHORT == 65535. */
88     WCHAR szValueName[sizeof("65535")];
89 
90     /* Open the Nls\CodePage key */
91     // #define REGSTR_PATH_CODEPAGE    TEXT("System\\CurrentControlSet\\Control\\Nls\\CodePage")
92     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
93                       L"System\\CurrentControlSet\\Control\\Nls\\CodePage",
94                       0,
95                       KEY_QUERY_VALUE,
96                       &hKey) != ERROR_SUCCESS)
97     {
98         return;
99     }
100 
101     ListCtl.hWndList = GetDlgItem(hDlg, IDL_CODEPAGE);
102     ListCtl.GetCount = List_GetCount;
103     ListCtl.GetData  = List_GetData;
104 
105     /* Enumerate all the available code pages on the system */
106     for (dwIndex = 0, cchValueName = ARRAYSIZE(szValueName);
107          (lResult = RegEnumValueW(hKey, dwIndex,
108                                   szValueName, &cchValueName,
109                                   NULL, &dwType,
110                                   NULL, NULL)) != ERROR_NO_MORE_ITEMS;
111          ++dwIndex, cchValueName = ARRAYSIZE(szValueName))
112     {
113         /* Ignore if we failed for another reason, e.g. because
114          * the value name is too long (and thus, invalid). */
115         if (lResult != ERROR_SUCCESS)
116             continue;
117 
118         /* Validate the value name (exclude the unnamed value) */
119         if (!cchValueName || (*szValueName == UNICODE_NULL))
120             continue;
121         /* Too large value names have already been handled with ERROR_MORE_DATA */
122         ASSERT((cchValueName < ARRAYSIZE(szValueName)) &&
123                (szValueName[cchValueName] == UNICODE_NULL));
124 
125         /* Validate the value type */
126         if (dwType != REG_SZ)
127             continue;
128 
129         /*
130          * Add the code page into the list.
131          * If _wtol fails and returns 0, the code page is considered invalid
132          * (and indeed this value corresponds to the CP_ACP alias too).
133          */
134         CodePage = (UINT)_wtol(szValueName);
135         if (CodePage == 0) continue;
136         AddCodePage(&ListCtl, CodePage);
137     }
138 
139     RegCloseKey(hKey);
140 
141     /* Add the special UTF-7 (CP_UTF7 65000) and UTF-8 (CP_UTF8 65001) code pages */
142     AddCodePage(&ListCtl, CP_UTF7);
143     AddCodePage(&ListCtl, CP_UTF8);
144 
145     /* Find and select the current code page in the sorted list */
146     if (BisectListSortedByValue(&ListCtl, CurrentCodePage, &CodePage, FALSE) == CB_ERR ||
147         CodePage == CB_ERR)
148     {
149         /* Not found, select the first element */
150         CodePage = 0;
151     }
152     SendMessageW(ListCtl.hWndList, CB_SETCURSEL, (WPARAM)CodePage, 0);
153 }
154 
155 static VOID
156 UpdateDialogElements(
157     IN HWND hDlg,
158     IN PCONSOLE_STATE_INFO pConInfo)
159 {
160     /* Update the cursor size */
161     if (pConInfo->CursorSize <= 25)
162     {
163         /* Small cursor */
164         CheckRadioButton(hDlg, IDC_RADIO_SMALL_CURSOR, IDC_RADIO_LARGE_CURSOR, IDC_RADIO_SMALL_CURSOR);
165         // CheckDlgButton(hDlg, IDC_RADIO_SMALL_CURSOR , BST_CHECKED);
166         // CheckDlgButton(hDlg, IDC_RADIO_MEDIUM_CURSOR, BST_UNCHECKED);
167         // CheckDlgButton(hDlg, IDC_RADIO_LARGE_CURSOR , BST_UNCHECKED);
168     }
169     else if (pConInfo->CursorSize <= 50)
170     {
171         /* Medium cursor */
172         CheckRadioButton(hDlg, IDC_RADIO_SMALL_CURSOR, IDC_RADIO_LARGE_CURSOR, IDC_RADIO_MEDIUM_CURSOR);
173         // CheckDlgButton(hDlg, IDC_RADIO_SMALL_CURSOR , BST_UNCHECKED);
174         // CheckDlgButton(hDlg, IDC_RADIO_MEDIUM_CURSOR, BST_CHECKED);
175         // CheckDlgButton(hDlg, IDC_RADIO_LARGE_CURSOR , BST_UNCHECKED);
176     }
177     else /* if (pConInfo->CursorSize <= 100) */
178     {
179         /* Large cursor */
180         CheckRadioButton(hDlg, IDC_RADIO_SMALL_CURSOR, IDC_RADIO_LARGE_CURSOR, IDC_RADIO_LARGE_CURSOR);
181         // CheckDlgButton(hDlg, IDC_RADIO_SMALL_CURSOR , BST_UNCHECKED);
182         // CheckDlgButton(hDlg, IDC_RADIO_MEDIUM_CURSOR, BST_UNCHECKED);
183         // CheckDlgButton(hDlg, IDC_RADIO_LARGE_CURSOR , BST_CHECKED);
184     }
185 
186     /* Update the number of history buffers */
187     SendDlgItemMessageW(hDlg, IDC_UPDOWN_NUM_BUFFER, UDM_SETRANGE, 0, MAKELONG(999, 1));
188     SetDlgItemInt(hDlg, IDC_EDIT_NUM_BUFFER, pConInfo->NumberOfHistoryBuffers, FALSE);
189 
190     /* Update the history buffer size */
191     SendDlgItemMessageW(hDlg, IDC_UPDOWN_BUFFER_SIZE, UDM_SETRANGE, 0, MAKELONG(999, 1));
192     SetDlgItemInt(hDlg, IDC_EDIT_BUFFER_SIZE, pConInfo->HistoryBufferSize, FALSE);
193 
194     /* Update discard duplicates */
195     CheckDlgButton(hDlg, IDC_CHECK_DISCARD_DUPLICATES,
196                    pConInfo->HistoryNoDup ? BST_CHECKED : BST_UNCHECKED);
197 
198     /* Update full/window screen state */
199     if (pConInfo->FullScreen)
200     {
201         CheckRadioButton(hDlg, IDC_RADIO_DISPLAY_WINDOW, IDC_RADIO_DISPLAY_FULL, IDC_RADIO_DISPLAY_FULL);
202         // CheckDlgButton(hDlg, IDC_RADIO_DISPLAY_WINDOW, BST_UNCHECKED);
203         // CheckDlgButton(hDlg, IDC_RADIO_DISPLAY_FULL  , BST_CHECKED);
204     }
205     else
206     {
207         CheckRadioButton(hDlg, IDC_RADIO_DISPLAY_WINDOW, IDC_RADIO_DISPLAY_FULL, IDC_RADIO_DISPLAY_WINDOW);
208         // CheckDlgButton(hDlg, IDC_RADIO_DISPLAY_WINDOW, BST_CHECKED);
209         // CheckDlgButton(hDlg, IDC_RADIO_DISPLAY_FULL  , BST_UNCHECKED);
210     }
211 
212     /* Update "Quick-edit" state */
213     CheckDlgButton(hDlg, IDC_CHECK_QUICK_EDIT,
214                    pConInfo->QuickEdit ? BST_CHECKED : BST_UNCHECKED);
215 
216     /* Update "Insert mode" state */
217     CheckDlgButton(hDlg, IDC_CHECK_INSERT_MODE,
218                    pConInfo->InsertMode ? BST_CHECKED : BST_UNCHECKED);
219 }
220 
221 INT_PTR
222 CALLBACK
223 OptionsProc(HWND hDlg,
224             UINT uMsg,
225             WPARAM wParam,
226             LPARAM lParam)
227 {
228     switch (uMsg)
229     {
230         case WM_INITDIALOG:
231         {
232             BuildCodePageList(hDlg, ConInfo->CodePage);
233             UpdateDialogElements(hDlg, ConInfo);
234             return TRUE;
235         }
236 
237         case WM_NOTIFY:
238         {
239             LPPSHNOTIFY lppsn = (LPPSHNOTIFY)lParam;
240 
241             if (lppsn->hdr.code == UDN_DELTAPOS)
242             {
243                 LPNMUPDOWN lpnmud = (LPNMUPDOWN)lParam;
244 
245                 if (lppsn->hdr.idFrom == IDC_UPDOWN_BUFFER_SIZE)
246                 {
247                     lpnmud->iPos = min(max(lpnmud->iPos + lpnmud->iDelta, 1), 999);
248                     ConInfo->HistoryBufferSize = lpnmud->iPos;
249                     PropSheet_Changed(GetParent(hDlg), hDlg);
250                 }
251                 else if (lppsn->hdr.idFrom == IDC_UPDOWN_NUM_BUFFER)
252                 {
253                     lpnmud->iPos = min(max(lpnmud->iPos + lpnmud->iDelta, 1), 999);
254                     ConInfo->NumberOfHistoryBuffers = lpnmud->iPos;
255                     PropSheet_Changed(GetParent(hDlg), hDlg);
256                 }
257             }
258             else if (lppsn->hdr.code == PSN_APPLY)
259             {
260                 ApplyConsoleInfo(hDlg);
261                 return TRUE;
262             }
263             break;
264         }
265 
266         case WM_COMMAND:
267         {
268             if (HIWORD(wParam) == BN_CLICKED)
269             {
270                 switch (LOWORD(wParam))
271                 {
272                 case IDC_RADIO_SMALL_CURSOR:
273                 {
274                     ConInfo->CursorSize = 25;
275                     PropSheet_Changed(GetParent(hDlg), hDlg);
276                     break;
277                 }
278                 case IDC_RADIO_MEDIUM_CURSOR:
279                 {
280                     ConInfo->CursorSize = 50;
281                     PropSheet_Changed(GetParent(hDlg), hDlg);
282                     break;
283                 }
284                 case IDC_RADIO_LARGE_CURSOR:
285                 {
286                     ConInfo->CursorSize = 100;
287                     PropSheet_Changed(GetParent(hDlg), hDlg);
288                     break;
289                 }
290                 case IDC_RADIO_DISPLAY_WINDOW:
291                 {
292                     ConInfo->FullScreen = FALSE;
293                     PropSheet_Changed(GetParent(hDlg), hDlg);
294                     break;
295                 }
296                 case IDC_RADIO_DISPLAY_FULL:
297                 {
298                     ConInfo->FullScreen = TRUE;
299                     PropSheet_Changed(GetParent(hDlg), hDlg);
300                     break;
301                 }
302                 case IDC_CHECK_QUICK_EDIT:
303                 {
304                     ConInfo->QuickEdit = (IsDlgButtonChecked(hDlg, IDC_CHECK_QUICK_EDIT) == BST_CHECKED); // BST_UNCHECKED or BST_INDETERMINATE => FALSE
305                     PropSheet_Changed(GetParent(hDlg), hDlg);
306                     break;
307                 }
308                 case IDC_CHECK_INSERT_MODE:
309                 {
310                     ConInfo->InsertMode = (IsDlgButtonChecked(hDlg, IDC_CHECK_INSERT_MODE) == BST_CHECKED); // BST_UNCHECKED or BST_INDETERMINATE => FALSE
311                     PropSheet_Changed(GetParent(hDlg), hDlg);
312                     break;
313                 }
314                 case IDC_CHECK_DISCARD_DUPLICATES:
315                 {
316                     ConInfo->HistoryNoDup = (IsDlgButtonChecked(hDlg, IDC_CHECK_DISCARD_DUPLICATES) == BST_CHECKED); // BST_UNCHECKED or BST_INDETERMINATE => FALSE
317                     PropSheet_Changed(GetParent(hDlg), hDlg);
318                     break;
319                 }
320                 }
321             }
322             else
323             if (HIWORD(wParam) == EN_KILLFOCUS)
324             {
325                 switch (LOWORD(wParam))
326                 {
327                 case IDC_EDIT_BUFFER_SIZE:
328                 {
329                     DWORD sizeBuff;
330 
331                     sizeBuff = GetDlgItemInt(hDlg, IDC_EDIT_BUFFER_SIZE, NULL, FALSE);
332                     sizeBuff = min(max(sizeBuff, 1), 999);
333 
334                     ConInfo->HistoryBufferSize = sizeBuff;
335                     PropSheet_Changed(GetParent(hDlg), hDlg);
336                     break;
337                 }
338                 case IDC_EDIT_NUM_BUFFER:
339                 {
340                     DWORD numBuff;
341 
342                     numBuff = GetDlgItemInt(hDlg, IDC_EDIT_NUM_BUFFER, NULL, FALSE);
343                     numBuff = min(max(numBuff, 1), 999);
344 
345                     ConInfo->NumberOfHistoryBuffers = numBuff;
346                     PropSheet_Changed(GetParent(hDlg), hDlg);
347                     break;
348                 }
349                 }
350             }
351             else
352             // (HIWORD(wParam) == CBN_KILLFOCUS)
353             if ((HIWORD(wParam) == CBN_SELCHANGE || HIWORD(wParam) == CBN_SELENDOK) &&
354                 (LOWORD(wParam) == IDL_CODEPAGE))
355             {
356                 HWND hWndList = GetDlgItem(hDlg, IDL_CODEPAGE);
357                 INT iItem;
358                 UINT CodePage;
359 
360                 iItem = (INT)SendMessageW(hWndList, CB_GETCURSEL, 0, 0);
361                 if (iItem == CB_ERR)
362                     break;
363 
364                 CodePage = (UINT)SendMessageW(hWndList, CB_GETITEMDATA, iItem, 0);
365                 if (CodePage == CB_ERR)
366                     break;
367 
368                 /* If the user has selected a different code page... */
369                 if ((HIWORD(wParam) == CBN_SELENDOK) && (CodePage != ConInfo->CodePage))
370                 {
371                     /* ... update the code page and change the property sheet state */
372                     ConInfo->CodePage = CodePage;
373                     ResetFontPreview(&FontPreview);
374                     PropSheet_Changed(GetParent(hDlg), hDlg);
375                 }
376             }
377 
378             break;
379         }
380 
381         default:
382             break;
383     }
384 
385     return FALSE;
386 }
387