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 } 1604