[APPLICATIONS/SYSUTILS] Add a font substitutes editor. Patch by Katayama Hirofumi...
[reactos.git] / rosapps / applications / sysutils / fontsub / fontsub.cpp
1 // FontSub by Katayama Hirofumi MZ
2 //
3 // To the extent possible under law, the person who associated CC0 with
4 // FontSub has waived all copyright and related or neighboring rights
5 // to FontSub.
6 //
7 // You should have received a copy of the CC0 legalcode along with this
8 // work. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
9
10
11 #include <windows.h>
12 #include <windowsx.h>
13 #include <commctrl.h>
14 #include <tchar.h>
15 #include <vector> // for std::vector
16 #include <set> // for std::set
17 #include <string> // for std::basic_string
18 #include <algorithm> // for std::sort
19 #include <cstdio>
20 #include <cstring>
21 #include <cassert>
22 #include "resource.h"
23
24
25 #define NAME_COLUMN_WIDTH 250
26 #define SUB_COLUMN_WIDTH 250
27 #define MAX_STRING 120
28
29 #ifndef _countof
30 #define _countof(array) (sizeof(array) / sizeof(array[0]))
31 #endif
32
33 typedef std::wstring STRING;
34
35 struct ITEM
36 {
37 STRING m_Name, m_Substitute;
38 BYTE m_CharSet1, m_CharSet2;
39 ITEM(const STRING& Name, const STRING& Substitute,
40 BYTE CharSet1, BYTE CharSet2)
41 : m_Name(Name), m_Substitute(Substitute),
42 m_CharSet1(CharSet1), m_CharSet2(CharSet2) { }
43 };
44
45 /* global variables */
46 HINSTANCE g_hInstance = NULL;
47 HWND g_hMainWnd = NULL;
48 HICON g_hIcon = NULL;
49 HWND g_hListView = NULL;
50 BOOL g_bModified = FALSE;
51 BOOL g_bNeedsReboot = FALSE;
52 INT g_iItem = 0;
53
54 LPCWSTR g_pszClassName = L"ReactOS Font Substitutes Editor";
55 LPCWSTR g_pszFileHeader = L"Windows Registry Editor Version 5.00";
56
57 WCHAR g_szTitle[MAX_STRING];
58 WCHAR g_szNameHead[MAX_STRING];
59 WCHAR g_szSubstituteHead[MAX_STRING];
60
61 INT g_iSortColumn = 0;
62 BOOL g_bSortAscendant = TRUE;
63
64 LPCWSTR g_pszKey =
65 L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes";
66
67 typedef std::set<STRING> FONTNAMESET;
68 typedef std::vector<ITEM> ITEMVECTOR;
69
70 FONTNAMESET g_Names;
71 ITEMVECTOR g_Items;
72 STRING g_strFontName;
73 STRING g_strSubstitute;
74 BYTE g_CharSet1 = DEFAULT_CHARSET;
75 BYTE g_CharSet2 = DEFAULT_CHARSET;
76
77 typedef struct CHARSET_ENTRY
78 {
79 BYTE CharSet;
80 LPCWSTR DisplayName;
81 } CHARSET_ENTRY;
82
83 CHARSET_ENTRY g_CharSetList[] =
84 {
85 { DEFAULT_CHARSET, L"DEFAULT_CHARSET (1)" },
86 { ANSI_CHARSET, L"ANSI_CHARSET (0)" },
87 { SYMBOL_CHARSET, L"SYMBOL_CHARSET (2)" },
88 { SHIFTJIS_CHARSET, L"SHIFTJIS_CHARSET (128)" },
89 { HANGUL_CHARSET, L"HANGUL_CHARSET (129)" },
90 { GB2312_CHARSET, L"GB2312_CHARSET (134)" },
91 { CHINESEBIG5_CHARSET, L"CHINESEBIG5_CHARSET (136)" },
92 { OEM_CHARSET, L"OEM_CHARSET (255)" },
93 { JOHAB_CHARSET, L"JOHAB_CHARSET (130)" },
94 { HEBREW_CHARSET, L"HEBREW_CHARSET (177)" },
95 { ARABIC_CHARSET, L"ARABIC_CHARSET (178)" },
96 { GREEK_CHARSET, L"GREEK_CHARSET (161)" },
97 { TURKISH_CHARSET, L"TURKISH_CHARSET (162)" },
98 { VIETNAMESE_CHARSET, L"VIETNAMESE_CHARSET (163)" },
99 { THAI_CHARSET, L"THAI_CHARSET (222)" },
100 { EASTEUROPE_CHARSET, L"EASTEUROPE_CHARSET (238)" },
101 { RUSSIAN_CHARSET, L"RUSSIAN_CHARSET (204)" },
102 { MAC_CHARSET, L"MAC_CHARSET (77)" },
103 { BALTIC_CHARSET, L"BALTIC_CHARSET (186)" }
104 };
105 const WCHAR g_LongestName[] = L"CHINESEBIG5_CHARSET (136)";
106
107 static void trim(STRING& str)
108 {
109 static const WCHAR Spaces[] = L" \t\r\n";
110 size_t i = str.find_first_not_of(Spaces);
111 size_t j = str.find_last_not_of(Spaces);
112 if (i == STRING::npos || j == STRING::npos)
113 {
114 str.clear();
115 }
116 else
117 {
118 str = str.substr(i, j - i + 1);
119 }
120 }
121
122 static int CALLBACK
123 EnumFontFamExProc(const ENUMLOGFONTW *pelf,
124 const NEWTEXTMETRICW *pntm,
125 int FontType,
126 LPARAM lParam)
127 {
128 switch (pelf->elfFullName[0])
129 {
130 case UNICODE_NULL: case L'@':
131 break;
132 default:
133 g_Names.insert((const WCHAR *)pelf->elfFullName);
134 }
135 switch (pelf->elfLogFont.lfFaceName[0])
136 {
137 case UNICODE_NULL: case L'@':
138 break;
139 default:
140 g_Names.insert(pelf->elfLogFont.lfFaceName);
141 }
142 return 1;
143 }
144
145 BOOL DoLoadNames(void)
146 {
147 g_Names.clear();
148
149 LOGFONTW lf;
150 ZeroMemory(&lf, sizeof(lf));
151 lf.lfCharSet = DEFAULT_CHARSET;
152
153 HDC hDC = CreateCompatibleDC(NULL);
154 EnumFontFamiliesExW(hDC, &lf, (FONTENUMPROCW)EnumFontFamExProc, 0, 0);
155 DeleteDC(hDC);
156
157 return !g_Names.empty();
158 }
159
160 inline bool ItemCompareByNameAscend(const ITEM& Item1, const ITEM& Item2)
161 {
162 return Item1.m_Name < Item2.m_Name;
163 }
164
165 inline bool ItemCompareByNameDescend(const ITEM& Item1, const ITEM& Item2)
166 {
167 return Item1.m_Name > Item2.m_Name;
168 }
169
170 inline bool ItemCompareBySubAscend(const ITEM& Item1, const ITEM& Item2)
171 {
172 return Item1.m_Substitute < Item2.m_Substitute;
173 }
174
175 inline bool ItemCompareBySubDescend(const ITEM& Item1, const ITEM& Item2)
176 {
177 return Item1.m_Substitute > Item2.m_Substitute;
178 }
179
180 void DoSort(INT iColumn, BOOL bAscendant = TRUE)
181 {
182 LV_COLUMN Column;
183 ZeroMemory(&Column, sizeof(Column));
184 Column.mask = LVCF_IMAGE | LVCF_SUBITEM;
185 Column.iImage = 2;
186 Column.iSubItem = 0;
187 ListView_SetColumn(g_hListView, 0, &Column);
188 Column.iSubItem = 1;
189 ListView_SetColumn(g_hListView, 1, &Column);
190
191 switch (iColumn)
192 {
193 case 0:
194 Column.iSubItem = 0;
195 if (bAscendant)
196 {
197 std::sort(g_Items.begin(), g_Items.end(),
198 ItemCompareByNameAscend);
199 Column.iImage = 0;
200 ListView_SetColumn(g_hListView, 0, &Column);
201 }
202 else
203 {
204 std::sort(g_Items.begin(), g_Items.end(),
205 ItemCompareByNameDescend);
206 Column.iImage = 1;
207 ListView_SetColumn(g_hListView, 0, &Column);
208 }
209 break;
210 case 1:
211 Column.iSubItem = 1;
212 if (bAscendant)
213 {
214 std::sort(g_Items.begin(), g_Items.end(),
215 ItemCompareBySubAscend);
216 Column.iImage = 0;
217 ListView_SetColumn(g_hListView, 1, &Column);
218 }
219 else
220 {
221 std::sort(g_Items.begin(), g_Items.end(),
222 ItemCompareBySubDescend);
223 Column.iImage = 1;
224 ListView_SetColumn(g_hListView, 1, &Column);
225 }
226 break;
227 }
228 g_iSortColumn = iColumn;
229 g_bSortAscendant = bAscendant;
230 InvalidateRect(g_hListView, NULL, TRUE);
231 }
232
233 void LV_AddItems(HWND hwnd)
234 {
235 ListView_DeleteAllItems(hwnd);
236
237 LV_ITEM Item;
238 ZeroMemory(&Item, sizeof(Item));
239 Item.mask = LVIF_PARAM;
240
241 const INT Count = INT(g_Items.size());
242 for (INT i = 0; i < Count; ++i)
243 {
244 Item.iItem = i;
245 Item.iSubItem = 0;
246 Item.lParam = i;
247 ListView_InsertItem(hwnd, &Item);
248
249 Item.iItem = i;
250 Item.iSubItem = 1;
251 Item.lParam = i;
252 ListView_InsertItem(hwnd, &Item);
253 }
254 }
255
256 BOOL DoLoadItems(void)
257 {
258 ITEMVECTOR Items;
259
260 HKEY hKey = NULL;
261 RegOpenKeyExW(HKEY_LOCAL_MACHINE, g_pszKey, 0, KEY_READ, &hKey);
262 if (hKey == NULL)
263 return FALSE;
264
265 WCHAR szName[MAX_STRING], szValue[MAX_STRING];
266 DWORD cbName, cbValue;
267 for (DWORD dwIndex = 0; ; ++dwIndex)
268 {
269 cbName = sizeof(szName);
270 cbValue = sizeof(szValue);
271 LONG Error = RegEnumValueW(hKey, dwIndex, szName, &cbName,
272 NULL, NULL, (LPBYTE)szValue, &cbValue);
273 if (Error != ERROR_SUCCESS)
274 break;
275
276 BYTE CharSet1 = DEFAULT_CHARSET, CharSet2 = DEFAULT_CHARSET;
277 LPWSTR pch;
278
279 pch = wcsrchr(szName, L',');
280 if (pch)
281 {
282 *pch = 0;
283 CharSet1 = (BYTE)_wtoi(pch + 1);
284 }
285
286 pch = wcsrchr(szValue, L',');
287 if (pch)
288 {
289 *pch = 0;
290 CharSet2 = (BYTE)_wtoi(pch + 1);
291 }
292
293 ITEM Item(szName, szValue, CharSet1, CharSet2);
294 trim(Item.m_Name);
295 trim(Item.m_Substitute);
296 Items.push_back(Item);
297 }
298
299 RegCloseKey(hKey);
300
301 g_Items = Items;
302 LV_AddItems(g_hListView);
303 DoSort(0, TRUE);
304 g_bModified = FALSE;
305 g_bNeedsReboot = FALSE;
306
307 return !g_Items.empty();
308 }
309
310 BOOL DoLoad(void)
311 {
312 return DoLoadNames() && DoLoadItems();
313 }
314
315 void LV_InvalidateRow(HWND hwnd, INT iRow = -1)
316 {
317 if (iRow == -1)
318 iRow = ListView_GetNextItem(hwnd, -1, LVNI_SELECTED);
319 if (iRow == -1)
320 return;
321
322 RECT Rect;
323 LPRECT GccIsWhining = &Rect;
324 ListView_GetItemRect(hwnd, iRow, GccIsWhining, LVIR_BOUNDS);
325 InvalidateRect(hwnd, &Rect, FALSE);
326 }
327
328 BOOL LV_Init(HWND hwnd)
329 {
330 ListView_SetExtendedListViewStyle(hwnd,
331 LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
332
333 HIMAGELIST hImageList;
334 hImageList = ImageList_Create(12, 12, ILC_COLOR8 | ILC_MASK, 2, 2);
335
336 HBITMAP hbm;
337 hbm = (HBITMAP)LoadImage(g_hInstance, MAKEINTRESOURCE(2), IMAGE_BITMAP,
338 12, 12, LR_CREATEDIBSECTION | LR_LOADMAP3DCOLORS);
339 assert(hbm);
340 ImageList_AddMasked(hImageList, hbm, RGB(192, 192, 192));
341 DeleteObject(hbm);
342
343 hbm = (HBITMAP)LoadImage(g_hInstance, MAKEINTRESOURCE(3), IMAGE_BITMAP,
344 12, 12, LR_CREATEDIBSECTION | LR_LOADMAP3DCOLORS);
345 assert(hbm);
346 ImageList_AddMasked(hImageList, hbm, RGB(192, 192, 192));
347 DeleteObject(hbm);
348
349 hbm = (HBITMAP)LoadImage(g_hInstance, MAKEINTRESOURCE(4), IMAGE_BITMAP,
350 12, 12, LR_CREATEDIBSECTION | LR_LOADMAP3DCOLORS);
351 assert(hbm);
352 ImageList_AddMasked(hImageList, hbm, RGB(192, 192, 192));
353 DeleteObject(hbm);
354
355 ListView_SetImageList(hwnd, hImageList, LVSIL_SMALL);
356
357 LV_COLUMNW Column;
358 ZeroMemory(&Column, sizeof(Column));
359 Column.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH | LVCF_IMAGE;
360 Column.fmt = LVCFMT_LEFT;
361
362 Column.cx = NAME_COLUMN_WIDTH;
363 Column.pszText = g_szNameHead;
364 Column.iSubItem = 0;
365 Column.iImage = 0;
366 ListView_InsertColumn(hwnd, 0, &Column);
367
368 Column.cx = SUB_COLUMN_WIDTH;
369 Column.pszText = g_szSubstituteHead;
370 Column.iSubItem = 1;
371 Column.iImage = 2;
372 ListView_InsertColumn(hwnd, 1, &Column);
373
374 UINT State = LVIS_SELECTED | LVIS_FOCUSED;
375 ListView_SetItemState(hwnd, 0, State, State);
376
377 return TRUE;
378 }
379
380 BOOL EditDlg_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam)
381 {
382 COMBOBOXEXITEMW Item;
383 ZeroMemory(&Item, sizeof(Item));
384 Item.mask = CBEIF_TEXT;
385
386 FONTNAMESET::iterator it, end = g_Names.end();
387 for (it = g_Names.begin(); it != end; ++it)
388 {
389 Item.pszText = const_cast<LPWSTR>(it->c_str());
390 Item.iItem = ComboBox_GetCount(GetDlgItem(hwnd, cmb2));
391 SendDlgItemMessageW(hwnd, cmb2, CBEM_INSERTITEM, 0, (LPARAM)&Item);
392 }
393 SetDlgItemTextW(hwnd, edt1, g_strFontName.c_str());
394 SetDlgItemTextW(hwnd, cmb2, g_strSubstitute.c_str());
395
396 const INT Count = _countof(g_CharSetList);
397 for (INT i = 0; i < Count; ++i)
398 {
399 Item.pszText = const_cast<LPWSTR>(g_CharSetList[i].DisplayName);
400 Item.iItem = ComboBox_GetCount(GetDlgItem(hwnd, cmb3));
401 SendDlgItemMessageW(hwnd, cmb3, CBEM_INSERTITEM, 0, (LPARAM)&Item);
402 Item.iItem = ComboBox_GetCount(GetDlgItem(hwnd, cmb4));
403 SendDlgItemMessageW(hwnd, cmb4, CBEM_INSERTITEM, 0, (LPARAM)&Item);
404 }
405
406 SendDlgItemMessageW(hwnd, cmb3, CB_SETCURSEL, 0, 0);
407 SendDlgItemMessageW(hwnd, cmb4, CB_SETCURSEL, 0, 0);
408 for (INT i = 0; i < Count; ++i)
409 {
410 if (g_CharSet1 == g_CharSetList[i].CharSet)
411 {
412 SendDlgItemMessageW(hwnd, cmb3, CB_SETCURSEL, i, 0);
413 }
414 if (g_CharSet2 == g_CharSetList[i].CharSet)
415 {
416 SendDlgItemMessageW(hwnd, cmb4, CB_SETCURSEL, i, 0);
417 }
418 }
419
420 SIZE siz;
421 HDC hDC = CreateCompatibleDC(NULL);
422 SelectObject(hDC, GetStockObject(DEFAULT_GUI_FONT));
423 GetTextExtentPoint32W(hDC, g_LongestName, lstrlenW(g_LongestName), &siz);
424 DeleteDC(hDC);
425
426 SendDlgItemMessageW(hwnd, cmb3, CB_SETHORIZONTALEXTENT, siz.cx + 16, 0);
427 SendDlgItemMessageW(hwnd, cmb4, CB_SETHORIZONTALEXTENT, siz.cx + 16, 0);
428
429 EnableWindow(GetDlgItem(hwnd, cmb3), FALSE);
430
431 return TRUE;
432 }
433
434 void LV_OnDelete(HWND hwnd, INT iRow = -1)
435 {
436 if (iRow == -1)
437 iRow = ListView_GetNextItem(hwnd, -1, LVNI_SELECTED);
438 if (iRow == -1)
439 return;
440
441 UINT State = LVIS_SELECTED | LVIS_FOCUSED;
442 ListView_SetItemState(g_hListView, iRow, State, State);
443
444 WCHAR sz[MAX_STRING];
445 LoadStringW(g_hInstance, IDS_QUERYDELETE, sz, _countof(sz));
446 if (IDYES != MessageBoxW(g_hMainWnd, sz, g_szTitle,
447 MB_ICONINFORMATION | MB_YESNO))
448 {
449 return;
450 }
451
452 ListView_DeleteItem(hwnd, iRow);
453 g_Items.erase(g_Items.begin() + iRow);
454 g_bModified = TRUE;
455
456 ListView_SetItemState(g_hListView, iRow, State, State);
457
458 InvalidateRect(hwnd, NULL, TRUE);
459 }
460
461 void EditDlg_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
462 {
463 WCHAR szValue[MAX_STRING];
464 STRING str;
465 INT i;
466
467 switch (id)
468 {
469 case IDOK:
470 GetDlgItemTextW(hwnd, cmb2, szValue, _countof(szValue));
471 str = szValue;
472 trim(str);
473 if (str.empty())
474 {
475 WCHAR sz[MAX_STRING];
476 SendDlgItemMessageW(hwnd, cmb2, CB_SETEDITSEL, 0, MAKELPARAM(0, -1));
477 SetFocus(GetDlgItem(hwnd, cmb2));
478 LoadStringW(g_hInstance, IDS_ENTERNAME2, sz, _countof(sz));
479 MessageBoxW(hwnd, sz, NULL, MB_ICONERROR);
480 return;
481 }
482
483 g_Items[g_iItem].m_CharSet2 = DEFAULT_CHARSET;
484 i = SendDlgItemMessageW(hwnd, cmb4, CB_GETCURSEL, 0, 0);
485 if (i != CB_ERR)
486 {
487 g_Items[g_iItem].m_CharSet2 = g_CharSetList[i].CharSet;
488 }
489 g_Items[g_iItem].m_Substitute = str;
490
491 g_bModified = TRUE;
492 EndDialog(hwnd, IDOK);
493 break;
494 case IDCANCEL:
495 EndDialog(hwnd, IDCANCEL);
496 break;
497 case psh1:
498 LV_OnDelete(g_hListView, g_iItem);
499 EndDialog(hwnd, psh1);
500 break;
501 }
502 }
503
504 INT_PTR CALLBACK
505 EditDlg_DlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
506 {
507 switch (uMsg)
508 {
509 HANDLE_MSG(hwnd, WM_INITDIALOG, EditDlg_OnInitDialog);
510 HANDLE_MSG(hwnd, WM_COMMAND, EditDlg_OnCommand);
511 }
512 return 0;
513 }
514
515 void LV_OnDblClk(HWND hwnd)
516 {
517 g_iItem = ListView_GetNextItem(hwnd, -1, LVNI_SELECTED);
518 if (g_iItem == -1)
519 return;
520
521 g_strFontName = g_Items[g_iItem].m_Name;
522 g_strSubstitute = g_Items[g_iItem].m_Substitute;
523 g_CharSet1 = g_Items[g_iItem].m_CharSet1;
524 g_CharSet2 = g_Items[g_iItem].m_CharSet2;
525
526 DialogBox(g_hInstance, MAKEINTRESOURCE(IDD_EDIT), g_hMainWnd,
527 EditDlg_DlgProc);
528 InvalidateRect(g_hListView, NULL, TRUE);
529 }
530
531 BOOL MainWnd_OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct)
532 {
533 DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_VSCROLL |
534 LVS_SINGLESEL | LVS_REPORT | LVS_OWNERDRAWFIXED;
535 DWORD dwExStyle = WS_EX_CLIENTEDGE;
536 g_hListView = CreateWindowEx(dwExStyle, WC_LISTVIEW, NULL, dwStyle,
537 0, 0, 0, 0,
538 hwnd, (HMENU)1, g_hInstance, NULL);
539 if (g_hListView == NULL)
540 return FALSE;
541
542 if (!LV_Init(g_hListView))
543 return FALSE;
544
545 if (!DoLoad())
546 return FALSE;
547
548 UINT State = LVIS_SELECTED | LVIS_FOCUSED;
549 ListView_SetItemState(g_hListView, 0, State, State);
550 SetFocus(g_hListView);
551 return TRUE;
552 }
553
554 BOOL AddDlg_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam)
555 {
556 COMBOBOXEXITEMW Item;
557 ZeroMemory(&Item, sizeof(Item));
558 Item.iItem = -1;
559 Item.mask = CBEIF_TEXT;
560
561 FONTNAMESET::iterator it, end = g_Names.end();
562 for (it = g_Names.begin(); it != end; ++it)
563 {
564 Item.pszText = const_cast<LPWSTR>(it->c_str());
565 Item.iItem = ComboBox_GetCount(GetDlgItem(hwnd, cmb1));
566 SendDlgItemMessageW(hwnd, cmb1, CBEM_INSERTITEM, 0, (LPARAM)&Item);
567 Item.iItem = ComboBox_GetCount(GetDlgItem(hwnd, cmb2));
568 SendDlgItemMessageW(hwnd, cmb2, CBEM_INSERTITEM, 0, (LPARAM)&Item);
569 }
570 WCHAR szEnterName[MAX_STRING];
571 LoadStringW(g_hInstance, IDS_ENTERNAME, szEnterName, _countof(szEnterName));
572 SetDlgItemTextW(hwnd, cmb1, szEnterName);
573 SendDlgItemMessageW(hwnd, cmb1, CB_SETEDITSEL, 0, MAKELPARAM(0, -1));
574
575 const INT Count = _countof(g_CharSetList);
576 for (INT i = 0; i < Count; ++i)
577 {
578 Item.pszText = const_cast<LPWSTR>(g_CharSetList[i].DisplayName);
579 Item.iItem = ComboBox_GetCount(GetDlgItem(hwnd, cmb3));
580 SendDlgItemMessageW(hwnd, cmb3, CBEM_INSERTITEM, 0, (LPARAM)&Item);
581 Item.iItem = ComboBox_GetCount(GetDlgItem(hwnd, cmb4));
582 SendDlgItemMessageW(hwnd, cmb4, CBEM_INSERTITEM, 0, (LPARAM)&Item);
583 }
584
585 SendDlgItemMessageW(hwnd, cmb3, CB_SETCURSEL, 0, 0);
586 SendDlgItemMessageW(hwnd, cmb4, CB_SETCURSEL, 0, 0);
587 for (INT i = 0; i < Count; ++i)
588 {
589 if (g_CharSet1 == g_CharSetList[i].CharSet)
590 {
591 SendDlgItemMessageW(hwnd, cmb3, CB_SETCURSEL, i, 0);
592 }
593 if (g_CharSet2 == g_CharSetList[i].CharSet)
594 {
595 SendDlgItemMessageW(hwnd, cmb4, CB_SETCURSEL, i, 0);
596 }
597 }
598
599 SIZE siz;
600 HDC hDC = CreateCompatibleDC(NULL);
601 SelectObject(hDC, GetStockObject(DEFAULT_GUI_FONT));
602 GetTextExtentPoint32W(hDC, g_LongestName, lstrlenW(g_LongestName), &siz);
603 DeleteDC(hDC);
604
605 SendDlgItemMessageW(hwnd, cmb3, CB_SETHORIZONTALEXTENT, siz.cx + 16, 0);
606 SendDlgItemMessageW(hwnd, cmb4, CB_SETHORIZONTALEXTENT, siz.cx + 16, 0);
607
608 return TRUE;
609 }
610
611 void AddDlg_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
612 {
613 WCHAR szKey[MAX_STRING], szValue[MAX_STRING], sz[MAX_STRING];
614 INT i, iCharSet1, iCharSet2;
615 BYTE CharSet1, CharSet2;
616 STRING key, value;
617 switch (id)
618 {
619 case IDOK:
620 GetDlgItemTextW(hwnd, cmb1, szKey, _countof(szKey));
621 key = szKey;
622 trim(key);
623 LoadStringW(g_hInstance, IDS_ENTERNAME, sz, _countof(sz));
624 if (key.empty() || key == sz)
625 {
626 SendDlgItemMessageW(hwnd, cmb1, CB_SETEDITSEL, 0, MAKELPARAM(0, -1));
627 SetFocus(GetDlgItem(hwnd, cmb1));
628 LoadStringW(g_hInstance, IDS_ENTERNAME2, sz, _countof(sz));
629 MessageBoxW(hwnd, sz, NULL, MB_ICONERROR);
630 return;
631 }
632
633 GetDlgItemTextW(hwnd, cmb2, szValue, _countof(szValue));
634 value = szValue;
635 trim(value);
636 if (value.empty())
637 {
638 SendDlgItemMessageW(hwnd, cmb2, CB_SETEDITSEL, 0, MAKELPARAM(0, -1));
639 SetFocus(GetDlgItem(hwnd, cmb2));
640 LoadStringW(g_hInstance, IDS_ENTERNAME2, sz, _countof(sz));
641 MessageBoxW(hwnd, sz, NULL, MB_ICONERROR);
642 return;
643 }
644
645 iCharSet1 = SendDlgItemMessageW(hwnd, cmb3, CB_GETCURSEL, 0, 0);
646 if (iCharSet1 == CB_ERR)
647 iCharSet1 = 0;
648 iCharSet2 = SendDlgItemMessageW(hwnd, cmb4, CB_GETCURSEL, 0, 0);
649 if (iCharSet2 == CB_ERR)
650 iCharSet2 = 0;
651
652 CharSet1 = g_CharSetList[iCharSet1].CharSet;
653 CharSet2 = g_CharSetList[iCharSet2].CharSet;
654
655 for (i = 0; i < (INT)g_Items.size(); ++i)
656 {
657 if (g_Items[i].m_Name == key &&
658 g_Items[i].m_CharSet1 == CharSet1)
659 {
660 WCHAR sz[MAX_STRING];
661 SendDlgItemMessageW(hwnd, cmb1, CB_SETEDITSEL, 0, MAKELPARAM(0, -1));
662 SetFocus(GetDlgItem(hwnd, cmb1));
663 LoadStringW(g_hInstance, IDS_ALREADYEXISTS, sz, _countof(sz));
664 MessageBoxW(hwnd, sz, NULL, MB_ICONERROR);
665 return;
666 }
667 }
668 {
669 ITEM Item(key, value, CharSet1, CharSet2);
670 g_Items.push_back(Item);
671 g_bModified = TRUE;
672
673 i = (INT)g_Items.size();
674 LV_ITEM LvItem;
675 ZeroMemory(&LvItem, sizeof(LvItem));
676 LvItem.mask = LVIF_PARAM;
677 LvItem.iItem = i;
678 LvItem.lParam = i;
679
680 LvItem.iSubItem = 0;
681 ListView_InsertItem(g_hListView, &LvItem);
682
683 LvItem.iSubItem = 1;
684 ListView_InsertItem(g_hListView, &LvItem);
685 }
686 g_bModified = TRUE;
687 EndDialog(hwnd, IDOK);
688 break;
689 case IDCANCEL:
690 EndDialog(hwnd, IDCANCEL);
691 break;
692 }
693 }
694
695 INT_PTR CALLBACK
696 AddDlg_DlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
697 {
698 switch (uMsg)
699 {
700 HANDLE_MSG(hwnd, WM_INITDIALOG, AddDlg_OnInitDialog);
701 HANDLE_MSG(hwnd, WM_COMMAND, AddDlg_OnCommand);
702 }
703 return 0;
704 }
705
706 void MainWnd_OnNew(HWND hwnd)
707 {
708 g_iItem = ListView_GetNextItem(hwnd, -1, LVNI_SELECTED);
709 if (g_iItem == -1)
710 return;
711
712 g_strFontName = g_Items[g_iItem].m_Name;
713 g_strSubstitute = g_Items[g_iItem].m_Substitute;
714 g_CharSet1 = g_Items[g_iItem].m_CharSet1;
715 g_CharSet2 = g_Items[g_iItem].m_CharSet2;
716
717 if (IDOK == DialogBox(g_hInstance, MAKEINTRESOURCE(IDD_ADD), g_hMainWnd,
718 AddDlg_DlgProc))
719 {
720 INT i = ListView_GetItemCount(g_hListView) - 1;
721 UINT State = LVIS_SELECTED | LVIS_FOCUSED;
722 ListView_SetItemState(g_hListView, i, State, State);
723 ListView_EnsureVisible(g_hListView, i, FALSE);
724 }
725 }
726
727 BOOL MainWnd_OnUpdateRegistry(HWND hwnd)
728 {
729 // open the key
730 HKEY hKey = NULL;
731 RegOpenKeyExW(HKEY_LOCAL_MACHINE, g_pszKey, 0, KEY_ALL_ACCESS, &hKey);
732 if (hKey == NULL)
733 return FALSE;
734
735 // clear all values
736 WCHAR szName[MAX_STRING], szValue[MAX_STRING];
737 DWORD cbName, cbValue;
738 for (;;)
739 {
740 cbName = sizeof(szName);
741 cbValue = sizeof(szValue);
742 LONG Error = RegEnumValueW(hKey, 0, szName, &cbName,
743 NULL, NULL, (LPBYTE)szValue, &cbValue);
744 if (Error != ERROR_SUCCESS)
745 break;
746
747 RegDeleteValueW(hKey, szName);
748 }
749
750 // set values
751 size_t Count = g_Items.size();
752 for (size_t i = 0; i < Count; ++i)
753 {
754 DWORD cbData = (g_Items[i].m_Substitute.size() + 1) * sizeof(WCHAR);
755 RegSetValueExW(hKey, g_Items[i].m_Name.c_str(), 0,
756 REG_SZ, (LPBYTE)g_Items[i].m_Substitute.c_str(), cbData);
757 }
758
759 // close now
760 RegCloseKey(hKey);
761
762 g_bModified = FALSE;
763 g_bNeedsReboot = TRUE;
764 return TRUE;
765 }
766
767 LPWSTR SkipSpace(LPCWSTR pch)
768 {
769 while (*pch && wcschr(L" \t\r\n", *pch) != NULL)
770 {
771 ++pch;
772 }
773 return const_cast<LPWSTR>(pch);
774 }
775
776 LPWSTR SkipQuoted(LPWSTR pch)
777 {
778 ++pch; // L'"'
779 while (*pch)
780 {
781 if (*pch == L'"')
782 {
783 ++pch;
784 break;
785 }
786 if (*pch == L'\\')
787 {
788 ++pch;
789 }
790 ++pch;
791 }
792 return pch;
793 }
794
795 void UnescapeHex(const STRING& str, size_t& i, STRING& Ret, BOOL Unicode)
796 {
797 STRING Num;
798
799 // hexadecimal
800 if (iswxdigit(str[i]))
801 {
802 Num += str[i];
803 ++i;
804 if (iswxdigit(str[i]))
805 {
806 Num += str[i];
807 ++i;
808 if (Unicode)
809 {
810 if (iswxdigit(str[i]))
811 {
812 Num += str[i];
813 ++i;
814 if (iswxdigit(str[i]))
815 {
816 Num += str[i];
817 ++i;
818 }
819 }
820 }
821 }
822 }
823 if (!Num.empty())
824 {
825 Ret += (WCHAR)wcstoul(&Num[0], NULL, 16);
826 }
827 }
828
829 void UnescapeOther(const STRING& str, size_t& i, STRING& Ret)
830 {
831 STRING Num;
832
833 // check octal
834 if (L'0' <= str[i] && str[i] < L'8')
835 {
836 Num += str[i];
837 ++i;
838 if (L'0' <= str[i] && str[i] < L'8')
839 {
840 Num += str[i];
841 ++i;
842 if (L'0' <= str[i] && str[i] < L'8')
843 {
844 Num += str[i];
845 ++i;
846 }
847 }
848 }
849 if (Num.empty())
850 {
851 Ret += str[i];
852 ++i;
853 }
854 else
855 {
856 // octal
857 Ret += (WCHAR)wcstoul(&Num[0], NULL, 8);
858 }
859 }
860
861 // process escape sequence
862 void UnescapeChar(const STRING& str, size_t& i, STRING& Ret)
863 {
864 if (str[i] != L'\\')
865 {
866 Ret += str[i];
867 ++i;
868 return;
869 }
870
871 ++i;
872 switch (str[i])
873 {
874 case L'a': Ret += L'\a'; ++i; break;
875 case L'b': Ret += L'\b'; ++i; break;
876 case L'f': Ret += L'\f'; ++i; break;
877 case L'n': Ret += L'\n'; ++i; break;
878 case L'r': Ret += L'\r'; ++i; break;
879 case L't': Ret += L'\t'; ++i; break;
880 case L'v': Ret += L'\v'; ++i; break;
881 case L'x':
882 // hexidemical
883 ++i;
884 UnescapeHex(str, i, Ret, FALSE);
885 break;
886 case L'u':
887 // Unicode hexidemical
888 ++i;
889 UnescapeHex(str, i, Ret, TRUE);
890 break;
891 default:
892 // other case
893 UnescapeOther(str, i, Ret);
894 break;
895 }
896 }
897
898 STRING Unquote(const STRING& str)
899 {
900 if (str[0] != L'"')
901 return str;
902
903 STRING Ret;
904 size_t i = 1;
905 while (i < str.size())
906 {
907 if (str[i] == L'"' || str[i] == UNICODE_NULL)
908 break;
909
910 UnescapeChar(str, i, Ret);
911 }
912 return Ret;
913 }
914
915 BOOL DoParseFile(LPVOID pvContents, DWORD dwSize)
916 {
917 ITEMVECTOR Items;
918
919 LPWSTR pch, pchSep, pchStart = (LPWSTR)pvContents;
920
921 pchStart[dwSize / sizeof(WCHAR)] = UNICODE_NULL;
922
923 // check header
924 const DWORD cbHeader = lstrlenW(g_pszFileHeader) * sizeof(WCHAR);
925 if (memcmp(pchStart, g_pszFileHeader, cbHeader) != 0)
926 return FALSE;
927
928 pchStart += cbHeader / sizeof(WCHAR);
929
930 // find the key
931 WCHAR szKey[MAX_STRING];
932 wsprintfW(szKey, L"[HKEY_LOCAL_MACHINE\\%s]", g_pszKey);
933 pch = wcsstr(pchStart, szKey);
934 if (pch == NULL)
935 return FALSE;
936
937 pchStart = pch + lstrlenW(szKey);
938
939 for (;;)
940 {
941 pchStart = SkipSpace(pchStart);
942 if (*pchStart == UNICODE_NULL || *pchStart == L'[')
943 break;
944
945 pch = wcschr(pchStart, L'\n');
946 if (pch)
947 *pch = UNICODE_NULL;
948
949 pchSep = SkipQuoted(pchStart);
950 if (*pchSep == L'=')
951 {
952 *pchSep = UNICODE_NULL;
953
954 STRING key = pchStart;
955 trim(key);
956 key = Unquote(key);
957
958 STRING value = pchSep + 1;
959 trim(value);
960 value = Unquote(value);
961
962 BYTE CharSet1 = DEFAULT_CHARSET, CharSet2 = DEFAULT_CHARSET;
963
964 size_t pos;
965 pos = key.find(L',');
966 if (pos != STRING::npos)
967 {
968 CharSet1 = (BYTE)_wtoi(&key[pos + 1]);
969 key.resize(pos);
970 trim(key);
971 }
972 pos = value.find(L',');
973 if (pos != STRING::npos)
974 {
975 CharSet2 = (BYTE)_wtoi(&value[pos + 1]);
976 value.resize(pos);
977 trim(value);
978 }
979
980 ITEM Item(key, value, CharSet1, CharSet2);
981 Items.push_back(Item);
982 }
983
984 if (pch == NULL)
985 break;
986
987 pchStart = pch + 1;
988 }
989
990 g_Items = Items;
991 g_bModified = TRUE;
992
993 LV_AddItems(g_hListView);
994 return TRUE;
995 }
996
997 BOOL DoImport(HWND hwnd, LPCWSTR pszFile)
998 {
999 HANDLE hFile = CreateFileW(pszFile, GENERIC_READ,
1000 FILE_SHARE_READ | FILE_SHARE_DELETE, NULL,
1001 OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
1002 if (hFile == INVALID_HANDLE_VALUE)
1003 return FALSE;
1004
1005 BOOL bSuccess = FALSE;
1006 DWORD dwSize = GetFileSize(hFile, NULL);
1007 if (dwSize != 0xFFFFFFFF)
1008 {
1009 std::vector<BYTE> Contents(dwSize + 2);
1010 DWORD cbRead;
1011 if (ReadFile(hFile, &Contents[0], dwSize, &cbRead, NULL) &&
1012 cbRead == dwSize)
1013 {
1014 /* check BOM */
1015 if (memcmp(&Contents[0], "\xFF\xFE", 2) == 0)
1016 {
1017 bSuccess = DoParseFile(&Contents[2], dwSize - 2);
1018 }
1019 else
1020 {
1021 bSuccess = DoParseFile(&Contents[0], dwSize);
1022 }
1023 }
1024 }
1025 CloseHandle(hFile);
1026
1027 return bSuccess;
1028 }
1029
1030 STRING Escape(const STRING& str)
1031 {
1032 STRING Ret;
1033 for (size_t i = 0; i < str.size(); ++i)
1034 {
1035 switch (str[i])
1036 {
1037 case L'"': case L'\\':
1038 Ret += L'\\';
1039 Ret += str[i];
1040 break;
1041 default:
1042 Ret += str[i];
1043 }
1044 }
1045 return Ret;
1046 }
1047
1048 BOOL DoExport(HWND hwnd, LPCWSTR pszFile)
1049 {
1050 HANDLE hFile = CreateFileW(pszFile, GENERIC_WRITE, FILE_SHARE_READ,
1051 NULL, CREATE_ALWAYS,
1052 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, NULL);
1053 if (hFile == INVALID_HANDLE_VALUE)
1054 return FALSE;
1055
1056 BOOL bSuccess;
1057 DWORD dwSize, cbWritten;
1058 WCHAR szCharSet1[MAX_STRING], szCharSet2[MAX_STRING];
1059 WCHAR szLine[MAX_STRING * 2 + 4];
1060
1061 /* write header */
1062 dwSize = lstrlenW(g_pszFileHeader) * sizeof(WCHAR);
1063 bSuccess =
1064 WriteFile(hFile, "\xFF\xFE", 2, &cbWritten, NULL) &&
1065 WriteFile(hFile, g_pszFileHeader, dwSize, &cbWritten, NULL);
1066 if (bSuccess)
1067 {
1068 wsprintfW(szLine, L"\r\n\r\n[HKEY_LOCAL_MACHINE\\%s]\r\n", g_pszKey);
1069 dwSize = lstrlenW(szLine) * sizeof(WCHAR);
1070 bSuccess = WriteFile(hFile, szLine, dwSize, &cbWritten, NULL);
1071 }
1072 if (bSuccess)
1073 {
1074 size_t i, Count = g_Items.size();
1075 for (i = 0; i < Count; ++i)
1076 {
1077 if (g_Items[i].m_CharSet1 != DEFAULT_CHARSET)
1078 wsprintfW(szCharSet1, L",%u", g_Items[i].m_CharSet1);
1079 else
1080 szCharSet1[0] = UNICODE_NULL;
1081
1082 if (g_Items[i].m_CharSet2 != DEFAULT_CHARSET)
1083 wsprintfW(szCharSet2, L",%u", g_Items[i].m_CharSet2);
1084 else
1085 szCharSet2[0] = UNICODE_NULL;
1086
1087 STRING Name = Escape(g_Items[i].m_Name);
1088 STRING Substitute = Escape(g_Items[i].m_Substitute);
1089 wsprintfW(szLine, L"\"%s%s\"=\"%s%s\"\r\n",
1090 Name.c_str(), szCharSet1,
1091 Substitute.c_str(), szCharSet2);
1092
1093 dwSize = lstrlenW(szLine) * sizeof(WCHAR);
1094 if (!WriteFile(hFile, szLine, dwSize, &cbWritten, NULL))
1095 {
1096 bSuccess = FALSE;
1097 break;
1098 }
1099 }
1100 WriteFile(hFile, L"\r\n", 2 * sizeof(WCHAR), &cbWritten, NULL);
1101 }
1102 CloseHandle(hFile);
1103
1104 if (!bSuccess)
1105 {
1106 DeleteFileW(pszFile);
1107 }
1108
1109 return bSuccess;
1110 }
1111
1112 void MakeFilter(LPWSTR pszFilter)
1113 {
1114 while (*pszFilter)
1115 {
1116 if (*pszFilter == L'|')
1117 *pszFilter = 0;
1118
1119 ++pszFilter;
1120 }
1121 }
1122
1123 void MainWnd_OnImport(HWND hwnd)
1124 {
1125 OPENFILENAMEW ofn = {0};
1126 WCHAR szFile[MAX_PATH] = L"";
1127 WCHAR szImportTitle[MAX_STRING];
1128 WCHAR szCannotImport[MAX_STRING];
1129 WCHAR szImportFilter[MAX_STRING];
1130 LoadStringW(g_hInstance, IDS_IMPORT, szImportTitle, _countof(szImportTitle));
1131 LoadStringW(g_hInstance, IDS_CANTIMPORT, szCannotImport, _countof(szCannotImport));
1132 LoadStringW(g_hInstance, IDS_INPFILTER, szImportFilter, _countof(szImportFilter));
1133 MakeFilter(szImportFilter);
1134
1135 ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
1136 ofn.hwndOwner = hwnd;
1137 ofn.lpstrFilter = szImportFilter;
1138 ofn.lpstrFile = szFile;
1139 ofn.nMaxFile = _countof(szFile);
1140 ofn.lpstrTitle = szImportTitle;
1141 ofn.Flags = OFN_DONTADDTORECENT | OFN_ENABLESIZING |
1142 OFN_EXPLORER | OFN_FILEMUSTEXIST |
1143 OFN_HIDEREADONLY | OFN_LONGNAMES |
1144 OFN_PATHMUSTEXIST;
1145 ofn.lpstrDefExt = L"reg";
1146 if (GetOpenFileNameW(&ofn))
1147 {
1148 if (!DoImport(hwnd, szFile))
1149 {
1150 MessageBoxW(hwnd, szCannotImport, g_szTitle, MB_ICONERROR);
1151 }
1152 }
1153 }
1154
1155 void MainWnd_OnExport(HWND hwnd)
1156 {
1157 OPENFILENAMEW ofn = {0};
1158 WCHAR szFile[MAX_PATH] = L"";
1159 WCHAR szExportTitle[MAX_STRING];
1160 WCHAR szCannotExport[MAX_STRING];
1161 WCHAR szExportFilter[MAX_STRING];
1162 LoadStringW(g_hInstance, IDS_EXPORT, szExportTitle, _countof(szExportTitle));
1163 LoadStringW(g_hInstance, IDS_CANTEXPORT, szCannotExport, _countof(szCannotExport));
1164 LoadStringW(g_hInstance, IDS_OUTFILTER, szExportFilter, _countof(szExportFilter));
1165 MakeFilter(szExportFilter);
1166
1167 ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
1168 ofn.hwndOwner = hwnd;
1169 ofn.lpstrFilter = szExportFilter;
1170 ofn.lpstrFile = szFile;
1171 ofn.nMaxFile = _countof(szFile);
1172 ofn.lpstrTitle = szExportTitle;
1173 ofn.Flags = OFN_DONTADDTORECENT | OFN_ENABLESIZING |
1174 OFN_EXPLORER | OFN_HIDEREADONLY | OFN_LONGNAMES |
1175 OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT;
1176 ofn.lpstrDefExt = L"reg";
1177 if (GetSaveFileNameW(&ofn))
1178 {
1179 if (!DoExport(hwnd, szFile))
1180 {
1181 MessageBoxW(hwnd, szCannotExport, g_szTitle, MB_ICONERROR);
1182 }
1183 }
1184 }
1185
1186 void MainWnd_OnReload(HWND hwnd)
1187 {
1188 DoLoad();
1189 }
1190
1191 void MainWnd_OnEdit(HWND hwnd)
1192 {
1193 LV_OnDblClk(g_hListView);
1194 }
1195
1196 void MainWnd_OnDelete(HWND hwnd)
1197 {
1198 LV_OnDelete(g_hListView);
1199 }
1200
1201 void MainWnd_OnOpenRegKey(HWND hwnd)
1202 {
1203 static const WCHAR s_szRegeditKey[] =
1204 L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Applets\\Regedit";
1205 WCHAR sz[MAX_STRING];
1206
1207 // open regedit key
1208 HKEY hKey = NULL;
1209 LSTATUS Result = RegCreateKeyExW(HKEY_CURRENT_USER, s_szRegeditKey, 0,
1210 NULL, 0, KEY_WRITE, NULL, &hKey, NULL);
1211 if (Result != ERROR_SUCCESS)
1212 {
1213 LoadStringW(g_hInstance, IDS_CANTOPENKEY, sz, _countof(sz));
1214 MessageBoxW(hwnd, sz, NULL, MB_ICONERROR);
1215 return;
1216 }
1217
1218 // set LastKey value
1219 wsprintfW(sz, L"HKEY_LOCAL_MACHINE\\%s", g_pszKey);
1220 DWORD dwSize = sizeof(sz);
1221 Result = RegSetValueExW(hKey, L"LastKey", 0, REG_SZ,
1222 (LPBYTE)sz, dwSize);
1223
1224 // close now
1225 RegCloseKey(hKey);
1226
1227 if (Result != ERROR_SUCCESS)
1228 {
1229 LoadStringW(g_hInstance, IDS_CANTOPENKEY, sz, _countof(sz));
1230 MessageBoxW(hwnd, sz, NULL, MB_ICONERROR);
1231 return;
1232 }
1233
1234 // open by regedit
1235 ShellExecuteW(hwnd, NULL, L"regedit.exe", NULL, NULL, SW_SHOWNORMAL);
1236 }
1237
1238 void MainWnd_OnAbout(HWND hwnd)
1239 {
1240 WCHAR szAbout[MAX_PATH];
1241 LoadStringW(g_hInstance, IDS_ABOUT, szAbout, _countof(szAbout));
1242
1243 MSGBOXPARAMS Params;
1244 ZeroMemory(&Params, sizeof(Params));
1245 Params.cbSize = sizeof(Params);
1246 Params.hwndOwner = hwnd;
1247 Params.hInstance = g_hInstance;
1248 Params.lpszText = szAbout;
1249 Params.lpszCaption = g_szTitle;
1250 Params.dwStyle = MB_OK | MB_USERICON;
1251 Params.lpszIcon = MAKEINTRESOURCEW(1);
1252 Params.dwLanguageId = LANG_USER_DEFAULT;
1253 MessageBoxIndirectW(&Params);
1254 }
1255
1256 void MainWnd_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
1257 {
1258 switch (id)
1259 {
1260 case ID_NEW:
1261 MainWnd_OnNew(hwnd);
1262 break;
1263 case ID_EDIT:
1264 MainWnd_OnEdit(hwnd);
1265 break;
1266 case ID_EXIT:
1267 PostMessage(hwnd, WM_CLOSE, 0, 0);
1268 break;
1269 case ID_RELOAD:
1270 MainWnd_OnReload(hwnd);
1271 break;
1272 case ID_UPDATE_REGISTRY:
1273 MainWnd_OnUpdateRegistry(hwnd);
1274 break;
1275 case ID_DELETE:
1276 MainWnd_OnDelete(hwnd);
1277 break;
1278 case ID_IMPORT:
1279 MainWnd_OnImport(hwnd);
1280 break;
1281 case ID_EXPORT:
1282 MainWnd_OnExport(hwnd);
1283 break;
1284 case ID_OPEN_REGKEY:
1285 MainWnd_OnOpenRegKey(hwnd);
1286 break;
1287 case ID_ABOUT:
1288 MainWnd_OnAbout(hwnd);
1289 break;
1290 }
1291 }
1292
1293 void MainWnd_OnDestroy(HWND hwnd)
1294 {
1295 PostQuitMessage(0);
1296 }
1297
1298 void MainWnd_OnSize(HWND hwnd, UINT state, int cx, int cy)
1299 {
1300 MoveWindow(g_hListView, 0, 0, cx, cy, TRUE);
1301 }
1302
1303 void MainWnd_OnDrawItem(HWND hwnd, const DRAWITEMSTRUCT *lpDrawItem)
1304 {
1305 if (lpDrawItem->CtlType != ODT_LISTVIEW)
1306 return;
1307
1308 HDC hDC = lpDrawItem->hDC;
1309 SetBkMode(hDC, TRANSPARENT);
1310
1311 INT iColumn = 0, x, cx;
1312 RECT rcItem, rcSubItem, rcText;
1313 STRING Str;
1314
1315 x = -GetScrollPos(g_hListView, SB_HORZ);
1316
1317 rcItem = lpDrawItem->rcItem;
1318 if (lpDrawItem->itemState & ODS_SELECTED)
1319 {
1320 FillRect(hDC, &rcItem, (HBRUSH)(COLOR_HIGHLIGHT + 1));
1321 SetTextColor(hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
1322 }
1323 else
1324 {
1325 FillRect(hDC, &rcItem, (HBRUSH)(COLOR_WINDOW + 1));
1326 SetTextColor(hDC, GetSysColor(COLOR_WINDOWTEXT));
1327 }
1328
1329 cx = ListView_GetColumnWidth(g_hListView, iColumn);
1330 rcSubItem = rcItem;
1331 rcSubItem.left = x;
1332 rcSubItem.right = x + cx;
1333
1334 WCHAR sz[MAX_STRING];
1335
1336 rcText = rcSubItem;
1337 InflateRect(&rcText, -1, -1);
1338 Str = g_Items[lpDrawItem->itemID].m_Name;
1339 BYTE CharSet1 = g_Items[lpDrawItem->itemID].m_CharSet1;
1340 if (CharSet1 != DEFAULT_CHARSET)
1341 wsprintfW(sz, L"%s,%u", Str.c_str(), CharSet1);
1342 else
1343 wsprintfW(sz, L"%s", Str.c_str());
1344
1345 DrawTextW(hDC, sz, lstrlenW(sz), &rcText,
1346 DT_SINGLELINE | DT_LEFT | DT_VCENTER | DT_END_ELLIPSIS |
1347 DT_NOPREFIX);
1348
1349 x += cx;
1350 ++iColumn;
1351
1352 cx = ListView_GetColumnWidth(g_hListView, iColumn);
1353 rcSubItem = rcItem;
1354 rcSubItem.left = x;
1355 rcSubItem.right = x + cx;
1356
1357 rcText = rcSubItem;
1358 InflateRect(&rcText, -1, -1);
1359 Str = g_Items[lpDrawItem->itemID].m_Substitute;
1360 BYTE CharSet2 = g_Items[lpDrawItem->itemID].m_CharSet2;
1361 if (CharSet2 != DEFAULT_CHARSET)
1362 wsprintfW(sz, L"%s,%u", Str.c_str(), CharSet2);
1363 else
1364 wsprintfW(sz, L"%s", Str.c_str());
1365
1366 DrawTextW(hDC, sz, lstrlenW(sz), &rcText,
1367 DT_SINGLELINE | DT_LEFT | DT_VCENTER | DT_END_ELLIPSIS |
1368 DT_NOPREFIX);
1369 }
1370
1371 void MainWnd_OnMeasureItem(HWND hwnd, MEASUREITEMSTRUCT *lpMeasureItem)
1372 {
1373 if (lpMeasureItem->CtlType != ODT_LISTVIEW)
1374 return;
1375
1376 TEXTMETRIC tm;
1377 HDC hDC = GetDC(hwnd);
1378 GetTextMetrics(hDC, &tm);
1379 ReleaseDC(hwnd, hDC);
1380
1381 lpMeasureItem->itemHeight = tm.tmHeight * 4 / 3;
1382 }
1383
1384 LRESULT MainWnd_OnNotify(HWND hwnd, int idFrom, NMHDR *pnmhdr)
1385 {
1386 NM_LISTVIEW *pNMLV = (NM_LISTVIEW *)pnmhdr;
1387 LV_KEYDOWN *pLVKD = (LV_KEYDOWN *)pnmhdr;
1388
1389 switch (pnmhdr->code)
1390 {
1391 case LVN_COLUMNCLICK:
1392 if (pNMLV->iSubItem == g_iSortColumn)
1393 DoSort(pNMLV->iSubItem, !g_bSortAscendant);
1394 else
1395 DoSort(pNMLV->iSubItem, TRUE);
1396 break;
1397 case NM_DBLCLK:
1398 LV_OnDblClk(g_hListView);
1399 break;
1400 case LVN_KEYDOWN:
1401 if (pLVKD->wVKey == VK_RETURN) // [Enter] key
1402 {
1403 LV_OnDblClk(g_hListView);
1404 }
1405 if (pLVKD->wVKey == VK_DELETE) // [Del] key
1406 {
1407 LV_OnDelete(g_hListView);
1408 }
1409 break;
1410 }
1411 return 0;
1412 }
1413
1414 LRESULT MainWnd_OnContextMenu(HWND hwnd, HWND hwndContext, UINT xPos, UINT yPos)
1415 {
1416 POINT pt = {(INT)xPos, (INT)yPos};
1417 ScreenToClient(g_hListView, &pt);
1418 SendMessageW(g_hListView, WM_LBUTTONDOWN, 0, MAKELPARAM(pt.x, pt.y));
1419
1420 HMENU hMenu = LoadMenu(g_hInstance, MAKEINTRESOURCE(2));
1421 if (hMenu == NULL)
1422 return 0;
1423
1424 HMENU hSubMenu = GetSubMenu(hMenu, 0);
1425 if (hSubMenu == NULL)
1426 return 0;
1427
1428 SetForegroundWindow(hwnd);
1429 TrackPopupMenu(hSubMenu, TPM_LEFTALIGN | TPM_RIGHTBUTTON,
1430 xPos, yPos, 0, g_hMainWnd, NULL);
1431 PostMessage(g_hMainWnd, WM_NULL, 0, 0);
1432 return 0;
1433 }
1434
1435 void MainWnd_OnActivate(HWND hwnd, UINT state, HWND hwndActDeact, BOOL fMinimized)
1436 {
1437 if (state != WA_INACTIVE)
1438 {
1439 SetFocus(g_hListView);
1440 }
1441 }
1442
1443 BOOL EnableProcessPrivileges(LPCWSTR lpPrivilegeName, BOOL bEnable = TRUE)
1444 {
1445 HANDLE hToken;
1446 LUID luid;
1447 TOKEN_PRIVILEGES tokenPrivileges;
1448 BOOL Ret;
1449
1450 Ret = ::OpenProcessToken(::GetCurrentProcess(),
1451 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
1452 &hToken);
1453 if (!Ret)
1454 return Ret; // failure
1455
1456 Ret = ::LookupPrivilegeValueW(NULL, lpPrivilegeName, &luid);
1457 if (Ret)
1458 {
1459 tokenPrivileges.PrivilegeCount = 1;
1460 tokenPrivileges.Privileges[0].Luid = luid;
1461 tokenPrivileges.Privileges[0].Attributes = bEnable ? SE_PRIVILEGE_ENABLED : 0;
1462
1463 Ret = ::AdjustTokenPrivileges(hToken, FALSE, &tokenPrivileges, 0, 0, 0);
1464 }
1465
1466 ::CloseHandle(hToken);
1467 return Ret;
1468 }
1469
1470 void MainWnd_OnClose(HWND hwnd)
1471 {
1472 if (!g_bNeedsReboot && !g_bModified)
1473 {
1474 DestroyWindow(hwnd);
1475 return;
1476 }
1477
1478 if (g_bModified)
1479 {
1480 WCHAR szUpdateNow[MAX_STRING];
1481 LoadStringW(g_hInstance, IDS_QUERYUPDATE, szUpdateNow, _countof(szUpdateNow));
1482 INT id = MessageBoxW(hwnd, szUpdateNow, g_szTitle,
1483 MB_ICONINFORMATION | MB_YESNOCANCEL);
1484 switch (id)
1485 {
1486 case IDYES:
1487 MainWnd_OnUpdateRegistry(hwnd);
1488 break;
1489 case IDNO:
1490 break;
1491 case IDCANCEL:
1492 return;
1493 }
1494 }
1495
1496 if (g_bNeedsReboot)
1497 {
1498 WCHAR szRebootNow[MAX_STRING];
1499 LoadStringW(g_hInstance, IDS_REBOOTNOW, szRebootNow, _countof(szRebootNow));
1500 INT id = MessageBoxW(hwnd, szRebootNow, g_szTitle,
1501 MB_ICONINFORMATION | MB_YESNOCANCEL);
1502 switch (id)
1503 {
1504 case IDYES:
1505 EnableProcessPrivileges(SE_SHUTDOWN_NAME, TRUE);
1506 ::ExitWindowsEx(EWX_REBOOT | EWX_FORCE, 0);
1507 break;
1508 case IDNO:
1509 break;
1510 case IDCANCEL:
1511 return;
1512 }
1513 }
1514
1515 ::DestroyWindow(hwnd);
1516 }
1517
1518 LRESULT CALLBACK
1519 WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1520 {
1521 switch (uMsg)
1522 {
1523 HANDLE_MSG(hwnd, WM_CREATE, MainWnd_OnCreate);
1524 HANDLE_MSG(hwnd, WM_COMMAND, MainWnd_OnCommand);
1525 HANDLE_MSG(hwnd, WM_DESTROY, MainWnd_OnDestroy);
1526 HANDLE_MSG(hwnd, WM_SIZE, MainWnd_OnSize);
1527 HANDLE_MSG(hwnd, WM_DRAWITEM, MainWnd_OnDrawItem);
1528 HANDLE_MSG(hwnd, WM_MEASUREITEM, MainWnd_OnMeasureItem);
1529 HANDLE_MSG(hwnd, WM_NOTIFY, MainWnd_OnNotify);
1530 HANDLE_MSG(hwnd, WM_CONTEXTMENU, MainWnd_OnContextMenu);
1531 HANDLE_MSG(hwnd, WM_ACTIVATE, MainWnd_OnActivate);
1532 HANDLE_MSG(hwnd, WM_CLOSE, MainWnd_OnClose);
1533 default:
1534 return DefWindowProc(hwnd, uMsg, wParam, lParam);
1535 }
1536 }
1537
1538 INT WINAPI wWinMain(
1539 HINSTANCE hInstance,
1540 HINSTANCE hPrevInstance,
1541 LPWSTR lpCmdLine,
1542 INT nCmdShow)
1543 {
1544 g_hInstance = hInstance;
1545 InitCommonControls();
1546
1547 HACCEL hAccel = LoadAcceleratorsW(hInstance, MAKEINTRESOURCEW(1));
1548
1549 LoadStringW(hInstance, IDS_TITLE, g_szTitle, _countof(g_szTitle));
1550 LoadStringW(hInstance, IDS_FONTNAME, g_szNameHead, _countof(g_szNameHead));
1551 LoadStringW(hInstance, IDS_SUBSTITUTE, g_szSubstituteHead, _countof(g_szSubstituteHead));
1552
1553 WNDCLASSW wc = {0};
1554 wc.style = 0;
1555 wc.lpfnWndProc = WindowProc;
1556 wc.hInstance = hInstance;
1557 g_hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(1));
1558 wc.hIcon = g_hIcon;
1559 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
1560 wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
1561 wc.lpszMenuName = MAKEINTRESOURCEW(1);
1562 wc.lpszClassName = g_pszClassName;
1563 if (!RegisterClassW(&wc))
1564 {
1565 MessageBoxA(NULL, "ERROR: RegisterClass failed.", NULL, MB_ICONERROR);
1566 return 1;
1567 }
1568
1569 const DWORD dwStyle = WS_OVERLAPPEDWINDOW;
1570 INT Width = NAME_COLUMN_WIDTH + SUB_COLUMN_WIDTH +
1571 GetSystemMetrics(SM_CXVSCROLL) +
1572 GetSystemMetrics(SM_CXSIZEFRAME);
1573 INT Height = 320;
1574
1575 RECT Rect = { 0, 0, Width, Height };
1576 AdjustWindowRect(&Rect, dwStyle, TRUE);
1577 Width = Rect.right - Rect.left;
1578 Height = Rect.bottom - Rect.top;
1579
1580 g_hMainWnd = CreateWindowW(g_pszClassName, g_szTitle, dwStyle,
1581 CW_USEDEFAULT, CW_USEDEFAULT, Width, Height,
1582 NULL, NULL, hInstance, NULL);
1583 if (g_hMainWnd == NULL)
1584 {
1585 MessageBoxA(NULL, "ERROR: CreateWindow failed.", NULL, MB_ICONERROR);
1586 return 2;
1587 }
1588
1589 ShowWindow(g_hMainWnd, nCmdShow);
1590 UpdateWindow(g_hMainWnd);
1591
1592 MSG msg;
1593 while (GetMessage(&msg, NULL, 0, 0))
1594 {
1595 if (TranslateAccelerator(g_hMainWnd, hAccel, &msg))
1596 continue;
1597
1598 TranslateMessage(&msg);
1599 DispatchMessage(&msg);
1600 }
1601
1602 return (INT)msg.wParam;
1603 }