mirror of
https://github.com/reactos/reactos.git
synced 2024-11-01 20:32:36 +00:00
fd3b70fc42
svn path=/trunk/; revision=74187
1604 lines
44 KiB
C++
1604 lines
44 KiB
C++
// FontSub by Katayama Hirofumi MZ
|
|
//
|
|
// To the extent possible under law, the person who associated CC0 with
|
|
// FontSub has waived all copyright and related or neighboring rights
|
|
// to FontSub.
|
|
//
|
|
// You should have received a copy of the CC0 legalcode along with this
|
|
// work. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
|
|
|
|
|
|
#include <windows.h>
|
|
#include <windowsx.h>
|
|
#include <commctrl.h>
|
|
#include <tchar.h>
|
|
#include <vector> // for std::vector
|
|
#include <set> // for std::set
|
|
#include <string> // for std::basic_string
|
|
#include <algorithm> // for std::sort
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <cassert>
|
|
#include "resource.h"
|
|
|
|
|
|
#define NAME_COLUMN_WIDTH 250
|
|
#define SUB_COLUMN_WIDTH 250
|
|
#define MAX_STRING 120
|
|
|
|
#ifndef _countof
|
|
#define _countof(array) (sizeof(array) / sizeof(array[0]))
|
|
#endif
|
|
|
|
typedef std::wstring STRING;
|
|
|
|
struct ITEM
|
|
{
|
|
STRING m_Name, m_Substitute;
|
|
BYTE m_CharSet1, m_CharSet2;
|
|
ITEM(const STRING& Name, const STRING& Substitute,
|
|
BYTE CharSet1, BYTE CharSet2)
|
|
: m_Name(Name), m_Substitute(Substitute),
|
|
m_CharSet1(CharSet1), m_CharSet2(CharSet2) { }
|
|
};
|
|
|
|
/* global variables */
|
|
HINSTANCE g_hInstance = NULL;
|
|
HWND g_hMainWnd = NULL;
|
|
HICON g_hIcon = NULL;
|
|
HWND g_hListView = NULL;
|
|
BOOL g_bModified = FALSE;
|
|
BOOL g_bNeedsReboot = FALSE;
|
|
INT g_iItem = 0;
|
|
|
|
LPCWSTR g_pszClassName = L"ReactOS Font Substitutes Editor";
|
|
LPCWSTR g_pszFileHeader = L"Windows Registry Editor Version 5.00";
|
|
|
|
WCHAR g_szTitle[MAX_STRING];
|
|
WCHAR g_szNameHead[MAX_STRING];
|
|
WCHAR g_szSubstituteHead[MAX_STRING];
|
|
|
|
INT g_iSortColumn = 0;
|
|
BOOL g_bSortAscendant = TRUE;
|
|
|
|
LPCWSTR g_pszKey =
|
|
L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes";
|
|
|
|
typedef std::set<STRING> FONTNAMESET;
|
|
typedef std::vector<ITEM> ITEMVECTOR;
|
|
|
|
FONTNAMESET g_Names;
|
|
ITEMVECTOR g_Items;
|
|
STRING g_strFontName;
|
|
STRING g_strSubstitute;
|
|
BYTE g_CharSet1 = DEFAULT_CHARSET;
|
|
BYTE g_CharSet2 = DEFAULT_CHARSET;
|
|
|
|
typedef struct CHARSET_ENTRY
|
|
{
|
|
BYTE CharSet;
|
|
LPCWSTR DisplayName;
|
|
} CHARSET_ENTRY;
|
|
|
|
CHARSET_ENTRY g_CharSetList[] =
|
|
{
|
|
{ DEFAULT_CHARSET, L"DEFAULT_CHARSET (1)" },
|
|
{ ANSI_CHARSET, L"ANSI_CHARSET (0)" },
|
|
{ SYMBOL_CHARSET, L"SYMBOL_CHARSET (2)" },
|
|
{ SHIFTJIS_CHARSET, L"SHIFTJIS_CHARSET (128)" },
|
|
{ HANGUL_CHARSET, L"HANGUL_CHARSET (129)" },
|
|
{ GB2312_CHARSET, L"GB2312_CHARSET (134)" },
|
|
{ CHINESEBIG5_CHARSET, L"CHINESEBIG5_CHARSET (136)" },
|
|
{ OEM_CHARSET, L"OEM_CHARSET (255)" },
|
|
{ JOHAB_CHARSET, L"JOHAB_CHARSET (130)" },
|
|
{ HEBREW_CHARSET, L"HEBREW_CHARSET (177)" },
|
|
{ ARABIC_CHARSET, L"ARABIC_CHARSET (178)" },
|
|
{ GREEK_CHARSET, L"GREEK_CHARSET (161)" },
|
|
{ TURKISH_CHARSET, L"TURKISH_CHARSET (162)" },
|
|
{ VIETNAMESE_CHARSET, L"VIETNAMESE_CHARSET (163)" },
|
|
{ THAI_CHARSET, L"THAI_CHARSET (222)" },
|
|
{ EASTEUROPE_CHARSET, L"EASTEUROPE_CHARSET (238)" },
|
|
{ RUSSIAN_CHARSET, L"RUSSIAN_CHARSET (204)" },
|
|
{ MAC_CHARSET, L"MAC_CHARSET (77)" },
|
|
{ BALTIC_CHARSET, L"BALTIC_CHARSET (186)" }
|
|
};
|
|
const WCHAR g_LongestName[] = L"CHINESEBIG5_CHARSET (136)";
|
|
|
|
static void trim(STRING& str)
|
|
{
|
|
static const WCHAR Spaces[] = L" \t\r\n";
|
|
size_t i = str.find_first_not_of(Spaces);
|
|
size_t j = str.find_last_not_of(Spaces);
|
|
if (i == STRING::npos || j == STRING::npos)
|
|
{
|
|
str.clear();
|
|
}
|
|
else
|
|
{
|
|
str = str.substr(i, j - i + 1);
|
|
}
|
|
}
|
|
|
|
static int CALLBACK
|
|
EnumFontFamExProc(const ENUMLOGFONTW *pelf,
|
|
const NEWTEXTMETRICW *pntm,
|
|
int FontType,
|
|
LPARAM lParam)
|
|
{
|
|
switch (pelf->elfFullName[0])
|
|
{
|
|
case UNICODE_NULL: case L'@':
|
|
break;
|
|
default:
|
|
g_Names.insert((const WCHAR *)pelf->elfFullName);
|
|
}
|
|
switch (pelf->elfLogFont.lfFaceName[0])
|
|
{
|
|
case UNICODE_NULL: case L'@':
|
|
break;
|
|
default:
|
|
g_Names.insert(pelf->elfLogFont.lfFaceName);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
BOOL DoLoadNames(void)
|
|
{
|
|
g_Names.clear();
|
|
|
|
LOGFONTW lf;
|
|
ZeroMemory(&lf, sizeof(lf));
|
|
lf.lfCharSet = DEFAULT_CHARSET;
|
|
|
|
HDC hDC = CreateCompatibleDC(NULL);
|
|
EnumFontFamiliesExW(hDC, &lf, (FONTENUMPROCW)EnumFontFamExProc, 0, 0);
|
|
DeleteDC(hDC);
|
|
|
|
return !g_Names.empty();
|
|
}
|
|
|
|
inline bool ItemCompareByNameAscend(const ITEM& Item1, const ITEM& Item2)
|
|
{
|
|
return Item1.m_Name < Item2.m_Name;
|
|
}
|
|
|
|
inline bool ItemCompareByNameDescend(const ITEM& Item1, const ITEM& Item2)
|
|
{
|
|
return Item1.m_Name > Item2.m_Name;
|
|
}
|
|
|
|
inline bool ItemCompareBySubAscend(const ITEM& Item1, const ITEM& Item2)
|
|
{
|
|
return Item1.m_Substitute < Item2.m_Substitute;
|
|
}
|
|
|
|
inline bool ItemCompareBySubDescend(const ITEM& Item1, const ITEM& Item2)
|
|
{
|
|
return Item1.m_Substitute > Item2.m_Substitute;
|
|
}
|
|
|
|
void DoSort(INT iColumn, BOOL bAscendant = TRUE)
|
|
{
|
|
LV_COLUMN Column;
|
|
ZeroMemory(&Column, sizeof(Column));
|
|
Column.mask = LVCF_IMAGE | LVCF_SUBITEM;
|
|
Column.iImage = 2;
|
|
Column.iSubItem = 0;
|
|
ListView_SetColumn(g_hListView, 0, &Column);
|
|
Column.iSubItem = 1;
|
|
ListView_SetColumn(g_hListView, 1, &Column);
|
|
|
|
switch (iColumn)
|
|
{
|
|
case 0:
|
|
Column.iSubItem = 0;
|
|
if (bAscendant)
|
|
{
|
|
std::sort(g_Items.begin(), g_Items.end(),
|
|
ItemCompareByNameAscend);
|
|
Column.iImage = 0;
|
|
ListView_SetColumn(g_hListView, 0, &Column);
|
|
}
|
|
else
|
|
{
|
|
std::sort(g_Items.begin(), g_Items.end(),
|
|
ItemCompareByNameDescend);
|
|
Column.iImage = 1;
|
|
ListView_SetColumn(g_hListView, 0, &Column);
|
|
}
|
|
break;
|
|
case 1:
|
|
Column.iSubItem = 1;
|
|
if (bAscendant)
|
|
{
|
|
std::sort(g_Items.begin(), g_Items.end(),
|
|
ItemCompareBySubAscend);
|
|
Column.iImage = 0;
|
|
ListView_SetColumn(g_hListView, 1, &Column);
|
|
}
|
|
else
|
|
{
|
|
std::sort(g_Items.begin(), g_Items.end(),
|
|
ItemCompareBySubDescend);
|
|
Column.iImage = 1;
|
|
ListView_SetColumn(g_hListView, 1, &Column);
|
|
}
|
|
break;
|
|
}
|
|
g_iSortColumn = iColumn;
|
|
g_bSortAscendant = bAscendant;
|
|
InvalidateRect(g_hListView, NULL, TRUE);
|
|
}
|
|
|
|
void LV_AddItems(HWND hwnd)
|
|
{
|
|
ListView_DeleteAllItems(hwnd);
|
|
|
|
LV_ITEM Item;
|
|
ZeroMemory(&Item, sizeof(Item));
|
|
Item.mask = LVIF_PARAM;
|
|
|
|
const INT Count = INT(g_Items.size());
|
|
for (INT i = 0; i < Count; ++i)
|
|
{
|
|
Item.iItem = i;
|
|
Item.iSubItem = 0;
|
|
Item.lParam = i;
|
|
ListView_InsertItem(hwnd, &Item);
|
|
|
|
Item.iItem = i;
|
|
Item.iSubItem = 1;
|
|
Item.lParam = i;
|
|
ListView_InsertItem(hwnd, &Item);
|
|
}
|
|
}
|
|
|
|
BOOL DoLoadItems(void)
|
|
{
|
|
ITEMVECTOR Items;
|
|
|
|
HKEY hKey = NULL;
|
|
RegOpenKeyExW(HKEY_LOCAL_MACHINE, g_pszKey, 0, KEY_READ, &hKey);
|
|
if (hKey == NULL)
|
|
return FALSE;
|
|
|
|
WCHAR szName[MAX_STRING], szValue[MAX_STRING];
|
|
DWORD cbName, cbValue;
|
|
for (DWORD dwIndex = 0; ; ++dwIndex)
|
|
{
|
|
cbName = sizeof(szName);
|
|
cbValue = sizeof(szValue);
|
|
LONG Error = RegEnumValueW(hKey, dwIndex, szName, &cbName,
|
|
NULL, NULL, (LPBYTE)szValue, &cbValue);
|
|
if (Error != ERROR_SUCCESS)
|
|
break;
|
|
|
|
BYTE CharSet1 = DEFAULT_CHARSET, CharSet2 = DEFAULT_CHARSET;
|
|
LPWSTR pch;
|
|
|
|
pch = wcsrchr(szName, L',');
|
|
if (pch)
|
|
{
|
|
*pch = 0;
|
|
CharSet1 = (BYTE)_wtoi(pch + 1);
|
|
}
|
|
|
|
pch = wcsrchr(szValue, L',');
|
|
if (pch)
|
|
{
|
|
*pch = 0;
|
|
CharSet2 = (BYTE)_wtoi(pch + 1);
|
|
}
|
|
|
|
ITEM Item(szName, szValue, CharSet1, CharSet2);
|
|
trim(Item.m_Name);
|
|
trim(Item.m_Substitute);
|
|
Items.push_back(Item);
|
|
}
|
|
|
|
RegCloseKey(hKey);
|
|
|
|
g_Items = Items;
|
|
LV_AddItems(g_hListView);
|
|
DoSort(0, TRUE);
|
|
g_bModified = FALSE;
|
|
g_bNeedsReboot = FALSE;
|
|
|
|
return !g_Items.empty();
|
|
}
|
|
|
|
BOOL DoLoad(void)
|
|
{
|
|
return DoLoadNames() && DoLoadItems();
|
|
}
|
|
|
|
void LV_InvalidateRow(HWND hwnd, INT iRow = -1)
|
|
{
|
|
if (iRow == -1)
|
|
iRow = ListView_GetNextItem(hwnd, -1, LVNI_SELECTED);
|
|
if (iRow == -1)
|
|
return;
|
|
|
|
RECT Rect;
|
|
LPRECT GccIsWhining = &Rect;
|
|
ListView_GetItemRect(hwnd, iRow, GccIsWhining, LVIR_BOUNDS);
|
|
InvalidateRect(hwnd, &Rect, FALSE);
|
|
}
|
|
|
|
BOOL LV_Init(HWND hwnd)
|
|
{
|
|
ListView_SetExtendedListViewStyle(hwnd,
|
|
LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
|
|
|
|
HIMAGELIST hImageList;
|
|
hImageList = ImageList_Create(12, 12, ILC_COLOR8 | ILC_MASK, 2, 2);
|
|
|
|
HBITMAP hbm;
|
|
hbm = (HBITMAP)LoadImage(g_hInstance, MAKEINTRESOURCE(2), IMAGE_BITMAP,
|
|
12, 12, LR_CREATEDIBSECTION | LR_LOADMAP3DCOLORS);
|
|
assert(hbm);
|
|
ImageList_AddMasked(hImageList, hbm, RGB(192, 192, 192));
|
|
DeleteObject(hbm);
|
|
|
|
hbm = (HBITMAP)LoadImage(g_hInstance, MAKEINTRESOURCE(3), IMAGE_BITMAP,
|
|
12, 12, LR_CREATEDIBSECTION | LR_LOADMAP3DCOLORS);
|
|
assert(hbm);
|
|
ImageList_AddMasked(hImageList, hbm, RGB(192, 192, 192));
|
|
DeleteObject(hbm);
|
|
|
|
hbm = (HBITMAP)LoadImage(g_hInstance, MAKEINTRESOURCE(4), IMAGE_BITMAP,
|
|
12, 12, LR_CREATEDIBSECTION | LR_LOADMAP3DCOLORS);
|
|
assert(hbm);
|
|
ImageList_AddMasked(hImageList, hbm, RGB(192, 192, 192));
|
|
DeleteObject(hbm);
|
|
|
|
ListView_SetImageList(hwnd, hImageList, LVSIL_SMALL);
|
|
|
|
LV_COLUMNW Column;
|
|
ZeroMemory(&Column, sizeof(Column));
|
|
Column.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH | LVCF_IMAGE;
|
|
Column.fmt = LVCFMT_LEFT;
|
|
|
|
Column.cx = NAME_COLUMN_WIDTH;
|
|
Column.pszText = g_szNameHead;
|
|
Column.iSubItem = 0;
|
|
Column.iImage = 0;
|
|
ListView_InsertColumn(hwnd, 0, &Column);
|
|
|
|
Column.cx = SUB_COLUMN_WIDTH;
|
|
Column.pszText = g_szSubstituteHead;
|
|
Column.iSubItem = 1;
|
|
Column.iImage = 2;
|
|
ListView_InsertColumn(hwnd, 1, &Column);
|
|
|
|
UINT State = LVIS_SELECTED | LVIS_FOCUSED;
|
|
ListView_SetItemState(hwnd, 0, State, State);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL EditDlg_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam)
|
|
{
|
|
COMBOBOXEXITEMW Item;
|
|
ZeroMemory(&Item, sizeof(Item));
|
|
Item.mask = CBEIF_TEXT;
|
|
|
|
FONTNAMESET::iterator it, end = g_Names.end();
|
|
for (it = g_Names.begin(); it != end; ++it)
|
|
{
|
|
Item.pszText = const_cast<LPWSTR>(it->c_str());
|
|
Item.iItem = ComboBox_GetCount(GetDlgItem(hwnd, cmb2));
|
|
SendDlgItemMessageW(hwnd, cmb2, CBEM_INSERTITEM, 0, (LPARAM)&Item);
|
|
}
|
|
SetDlgItemTextW(hwnd, edt1, g_strFontName.c_str());
|
|
SetDlgItemTextW(hwnd, cmb2, g_strSubstitute.c_str());
|
|
|
|
const INT Count = _countof(g_CharSetList);
|
|
for (INT i = 0; i < Count; ++i)
|
|
{
|
|
Item.pszText = const_cast<LPWSTR>(g_CharSetList[i].DisplayName);
|
|
Item.iItem = ComboBox_GetCount(GetDlgItem(hwnd, cmb3));
|
|
SendDlgItemMessageW(hwnd, cmb3, CBEM_INSERTITEM, 0, (LPARAM)&Item);
|
|
Item.iItem = ComboBox_GetCount(GetDlgItem(hwnd, cmb4));
|
|
SendDlgItemMessageW(hwnd, cmb4, CBEM_INSERTITEM, 0, (LPARAM)&Item);
|
|
}
|
|
|
|
SendDlgItemMessageW(hwnd, cmb3, CB_SETCURSEL, 0, 0);
|
|
SendDlgItemMessageW(hwnd, cmb4, CB_SETCURSEL, 0, 0);
|
|
for (INT i = 0; i < Count; ++i)
|
|
{
|
|
if (g_CharSet1 == g_CharSetList[i].CharSet)
|
|
{
|
|
SendDlgItemMessageW(hwnd, cmb3, CB_SETCURSEL, i, 0);
|
|
}
|
|
if (g_CharSet2 == g_CharSetList[i].CharSet)
|
|
{
|
|
SendDlgItemMessageW(hwnd, cmb4, CB_SETCURSEL, i, 0);
|
|
}
|
|
}
|
|
|
|
SIZE siz;
|
|
HDC hDC = CreateCompatibleDC(NULL);
|
|
SelectObject(hDC, GetStockObject(DEFAULT_GUI_FONT));
|
|
GetTextExtentPoint32W(hDC, g_LongestName, lstrlenW(g_LongestName), &siz);
|
|
DeleteDC(hDC);
|
|
|
|
SendDlgItemMessageW(hwnd, cmb3, CB_SETHORIZONTALEXTENT, siz.cx + 16, 0);
|
|
SendDlgItemMessageW(hwnd, cmb4, CB_SETHORIZONTALEXTENT, siz.cx + 16, 0);
|
|
|
|
EnableWindow(GetDlgItem(hwnd, cmb3), FALSE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void LV_OnDelete(HWND hwnd, INT iRow = -1)
|
|
{
|
|
if (iRow == -1)
|
|
iRow = ListView_GetNextItem(hwnd, -1, LVNI_SELECTED);
|
|
if (iRow == -1)
|
|
return;
|
|
|
|
UINT State = LVIS_SELECTED | LVIS_FOCUSED;
|
|
ListView_SetItemState(g_hListView, iRow, State, State);
|
|
|
|
WCHAR sz[MAX_STRING];
|
|
LoadStringW(g_hInstance, IDS_QUERYDELETE, sz, _countof(sz));
|
|
if (IDYES != MessageBoxW(g_hMainWnd, sz, g_szTitle,
|
|
MB_ICONINFORMATION | MB_YESNO))
|
|
{
|
|
return;
|
|
}
|
|
|
|
ListView_DeleteItem(hwnd, iRow);
|
|
g_Items.erase(g_Items.begin() + iRow);
|
|
g_bModified = TRUE;
|
|
|
|
ListView_SetItemState(g_hListView, iRow, State, State);
|
|
|
|
InvalidateRect(hwnd, NULL, TRUE);
|
|
}
|
|
|
|
void EditDlg_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
|
|
{
|
|
WCHAR szValue[MAX_STRING];
|
|
STRING str;
|
|
INT i;
|
|
|
|
switch (id)
|
|
{
|
|
case IDOK:
|
|
GetDlgItemTextW(hwnd, cmb2, szValue, _countof(szValue));
|
|
str = szValue;
|
|
trim(str);
|
|
if (str.empty())
|
|
{
|
|
WCHAR sz[MAX_STRING];
|
|
SendDlgItemMessageW(hwnd, cmb2, CB_SETEDITSEL, 0, MAKELPARAM(0, -1));
|
|
SetFocus(GetDlgItem(hwnd, cmb2));
|
|
LoadStringW(g_hInstance, IDS_ENTERNAME2, sz, _countof(sz));
|
|
MessageBoxW(hwnd, sz, NULL, MB_ICONERROR);
|
|
return;
|
|
}
|
|
|
|
g_Items[g_iItem].m_CharSet2 = DEFAULT_CHARSET;
|
|
i = SendDlgItemMessageW(hwnd, cmb4, CB_GETCURSEL, 0, 0);
|
|
if (i != CB_ERR)
|
|
{
|
|
g_Items[g_iItem].m_CharSet2 = g_CharSetList[i].CharSet;
|
|
}
|
|
g_Items[g_iItem].m_Substitute = str;
|
|
|
|
g_bModified = TRUE;
|
|
EndDialog(hwnd, IDOK);
|
|
break;
|
|
case IDCANCEL:
|
|
EndDialog(hwnd, IDCANCEL);
|
|
break;
|
|
case psh1:
|
|
LV_OnDelete(g_hListView, g_iItem);
|
|
EndDialog(hwnd, psh1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
INT_PTR CALLBACK
|
|
EditDlg_DlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (uMsg)
|
|
{
|
|
HANDLE_MSG(hwnd, WM_INITDIALOG, EditDlg_OnInitDialog);
|
|
HANDLE_MSG(hwnd, WM_COMMAND, EditDlg_OnCommand);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void LV_OnDblClk(HWND hwnd)
|
|
{
|
|
g_iItem = ListView_GetNextItem(hwnd, -1, LVNI_SELECTED);
|
|
if (g_iItem == -1)
|
|
return;
|
|
|
|
g_strFontName = g_Items[g_iItem].m_Name;
|
|
g_strSubstitute = g_Items[g_iItem].m_Substitute;
|
|
g_CharSet1 = g_Items[g_iItem].m_CharSet1;
|
|
g_CharSet2 = g_Items[g_iItem].m_CharSet2;
|
|
|
|
DialogBox(g_hInstance, MAKEINTRESOURCE(IDD_EDIT), g_hMainWnd,
|
|
EditDlg_DlgProc);
|
|
InvalidateRect(g_hListView, NULL, TRUE);
|
|
}
|
|
|
|
BOOL MainWnd_OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct)
|
|
{
|
|
DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_VSCROLL |
|
|
LVS_SINGLESEL | LVS_REPORT | LVS_OWNERDRAWFIXED;
|
|
DWORD dwExStyle = WS_EX_CLIENTEDGE;
|
|
g_hListView = CreateWindowEx(dwExStyle, WC_LISTVIEW, NULL, dwStyle,
|
|
0, 0, 0, 0,
|
|
hwnd, (HMENU)1, g_hInstance, NULL);
|
|
if (g_hListView == NULL)
|
|
return FALSE;
|
|
|
|
if (!LV_Init(g_hListView))
|
|
return FALSE;
|
|
|
|
if (!DoLoad())
|
|
return FALSE;
|
|
|
|
UINT State = LVIS_SELECTED | LVIS_FOCUSED;
|
|
ListView_SetItemState(g_hListView, 0, State, State);
|
|
SetFocus(g_hListView);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL AddDlg_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam)
|
|
{
|
|
COMBOBOXEXITEMW Item;
|
|
ZeroMemory(&Item, sizeof(Item));
|
|
Item.iItem = -1;
|
|
Item.mask = CBEIF_TEXT;
|
|
|
|
FONTNAMESET::iterator it, end = g_Names.end();
|
|
for (it = g_Names.begin(); it != end; ++it)
|
|
{
|
|
Item.pszText = const_cast<LPWSTR>(it->c_str());
|
|
Item.iItem = ComboBox_GetCount(GetDlgItem(hwnd, cmb1));
|
|
SendDlgItemMessageW(hwnd, cmb1, CBEM_INSERTITEM, 0, (LPARAM)&Item);
|
|
Item.iItem = ComboBox_GetCount(GetDlgItem(hwnd, cmb2));
|
|
SendDlgItemMessageW(hwnd, cmb2, CBEM_INSERTITEM, 0, (LPARAM)&Item);
|
|
}
|
|
WCHAR szEnterName[MAX_STRING];
|
|
LoadStringW(g_hInstance, IDS_ENTERNAME, szEnterName, _countof(szEnterName));
|
|
SetDlgItemTextW(hwnd, cmb1, szEnterName);
|
|
SendDlgItemMessageW(hwnd, cmb1, CB_SETEDITSEL, 0, MAKELPARAM(0, -1));
|
|
|
|
const INT Count = _countof(g_CharSetList);
|
|
for (INT i = 0; i < Count; ++i)
|
|
{
|
|
Item.pszText = const_cast<LPWSTR>(g_CharSetList[i].DisplayName);
|
|
Item.iItem = ComboBox_GetCount(GetDlgItem(hwnd, cmb3));
|
|
SendDlgItemMessageW(hwnd, cmb3, CBEM_INSERTITEM, 0, (LPARAM)&Item);
|
|
Item.iItem = ComboBox_GetCount(GetDlgItem(hwnd, cmb4));
|
|
SendDlgItemMessageW(hwnd, cmb4, CBEM_INSERTITEM, 0, (LPARAM)&Item);
|
|
}
|
|
|
|
SendDlgItemMessageW(hwnd, cmb3, CB_SETCURSEL, 0, 0);
|
|
SendDlgItemMessageW(hwnd, cmb4, CB_SETCURSEL, 0, 0);
|
|
for (INT i = 0; i < Count; ++i)
|
|
{
|
|
if (g_CharSet1 == g_CharSetList[i].CharSet)
|
|
{
|
|
SendDlgItemMessageW(hwnd, cmb3, CB_SETCURSEL, i, 0);
|
|
}
|
|
if (g_CharSet2 == g_CharSetList[i].CharSet)
|
|
{
|
|
SendDlgItemMessageW(hwnd, cmb4, CB_SETCURSEL, i, 0);
|
|
}
|
|
}
|
|
|
|
SIZE siz;
|
|
HDC hDC = CreateCompatibleDC(NULL);
|
|
SelectObject(hDC, GetStockObject(DEFAULT_GUI_FONT));
|
|
GetTextExtentPoint32W(hDC, g_LongestName, lstrlenW(g_LongestName), &siz);
|
|
DeleteDC(hDC);
|
|
|
|
SendDlgItemMessageW(hwnd, cmb3, CB_SETHORIZONTALEXTENT, siz.cx + 16, 0);
|
|
SendDlgItemMessageW(hwnd, cmb4, CB_SETHORIZONTALEXTENT, siz.cx + 16, 0);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void AddDlg_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
|
|
{
|
|
WCHAR szKey[MAX_STRING], szValue[MAX_STRING], sz[MAX_STRING];
|
|
INT i, iCharSet1, iCharSet2;
|
|
BYTE CharSet1, CharSet2;
|
|
STRING key, value;
|
|
switch (id)
|
|
{
|
|
case IDOK:
|
|
GetDlgItemTextW(hwnd, cmb1, szKey, _countof(szKey));
|
|
key = szKey;
|
|
trim(key);
|
|
LoadStringW(g_hInstance, IDS_ENTERNAME, sz, _countof(sz));
|
|
if (key.empty() || key == sz)
|
|
{
|
|
SendDlgItemMessageW(hwnd, cmb1, CB_SETEDITSEL, 0, MAKELPARAM(0, -1));
|
|
SetFocus(GetDlgItem(hwnd, cmb1));
|
|
LoadStringW(g_hInstance, IDS_ENTERNAME2, sz, _countof(sz));
|
|
MessageBoxW(hwnd, sz, NULL, MB_ICONERROR);
|
|
return;
|
|
}
|
|
|
|
GetDlgItemTextW(hwnd, cmb2, szValue, _countof(szValue));
|
|
value = szValue;
|
|
trim(value);
|
|
if (value.empty())
|
|
{
|
|
SendDlgItemMessageW(hwnd, cmb2, CB_SETEDITSEL, 0, MAKELPARAM(0, -1));
|
|
SetFocus(GetDlgItem(hwnd, cmb2));
|
|
LoadStringW(g_hInstance, IDS_ENTERNAME2, sz, _countof(sz));
|
|
MessageBoxW(hwnd, sz, NULL, MB_ICONERROR);
|
|
return;
|
|
}
|
|
|
|
iCharSet1 = SendDlgItemMessageW(hwnd, cmb3, CB_GETCURSEL, 0, 0);
|
|
if (iCharSet1 == CB_ERR)
|
|
iCharSet1 = 0;
|
|
iCharSet2 = SendDlgItemMessageW(hwnd, cmb4, CB_GETCURSEL, 0, 0);
|
|
if (iCharSet2 == CB_ERR)
|
|
iCharSet2 = 0;
|
|
|
|
CharSet1 = g_CharSetList[iCharSet1].CharSet;
|
|
CharSet2 = g_CharSetList[iCharSet2].CharSet;
|
|
|
|
for (i = 0; i < (INT)g_Items.size(); ++i)
|
|
{
|
|
if (g_Items[i].m_Name == key &&
|
|
g_Items[i].m_CharSet1 == CharSet1)
|
|
{
|
|
WCHAR sz[MAX_STRING];
|
|
SendDlgItemMessageW(hwnd, cmb1, CB_SETEDITSEL, 0, MAKELPARAM(0, -1));
|
|
SetFocus(GetDlgItem(hwnd, cmb1));
|
|
LoadStringW(g_hInstance, IDS_ALREADYEXISTS, sz, _countof(sz));
|
|
MessageBoxW(hwnd, sz, NULL, MB_ICONERROR);
|
|
return;
|
|
}
|
|
}
|
|
{
|
|
ITEM Item(key, value, CharSet1, CharSet2);
|
|
g_Items.push_back(Item);
|
|
g_bModified = TRUE;
|
|
|
|
i = (INT)g_Items.size();
|
|
LV_ITEM LvItem;
|
|
ZeroMemory(&LvItem, sizeof(LvItem));
|
|
LvItem.mask = LVIF_PARAM;
|
|
LvItem.iItem = i;
|
|
LvItem.lParam = i;
|
|
|
|
LvItem.iSubItem = 0;
|
|
ListView_InsertItem(g_hListView, &LvItem);
|
|
|
|
LvItem.iSubItem = 1;
|
|
ListView_InsertItem(g_hListView, &LvItem);
|
|
}
|
|
g_bModified = TRUE;
|
|
EndDialog(hwnd, IDOK);
|
|
break;
|
|
case IDCANCEL:
|
|
EndDialog(hwnd, IDCANCEL);
|
|
break;
|
|
}
|
|
}
|
|
|
|
INT_PTR CALLBACK
|
|
AddDlg_DlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (uMsg)
|
|
{
|
|
HANDLE_MSG(hwnd, WM_INITDIALOG, AddDlg_OnInitDialog);
|
|
HANDLE_MSG(hwnd, WM_COMMAND, AddDlg_OnCommand);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void MainWnd_OnNew(HWND hwnd)
|
|
{
|
|
g_iItem = ListView_GetNextItem(hwnd, -1, LVNI_SELECTED);
|
|
if (g_iItem == -1)
|
|
return;
|
|
|
|
g_strFontName = g_Items[g_iItem].m_Name;
|
|
g_strSubstitute = g_Items[g_iItem].m_Substitute;
|
|
g_CharSet1 = g_Items[g_iItem].m_CharSet1;
|
|
g_CharSet2 = g_Items[g_iItem].m_CharSet2;
|
|
|
|
if (IDOK == DialogBox(g_hInstance, MAKEINTRESOURCE(IDD_ADD), g_hMainWnd,
|
|
AddDlg_DlgProc))
|
|
{
|
|
INT i = ListView_GetItemCount(g_hListView) - 1;
|
|
UINT State = LVIS_SELECTED | LVIS_FOCUSED;
|
|
ListView_SetItemState(g_hListView, i, State, State);
|
|
ListView_EnsureVisible(g_hListView, i, FALSE);
|
|
}
|
|
}
|
|
|
|
BOOL MainWnd_OnUpdateRegistry(HWND hwnd)
|
|
{
|
|
// open the key
|
|
HKEY hKey = NULL;
|
|
RegOpenKeyExW(HKEY_LOCAL_MACHINE, g_pszKey, 0, KEY_ALL_ACCESS, &hKey);
|
|
if (hKey == NULL)
|
|
return FALSE;
|
|
|
|
// clear all values
|
|
WCHAR szName[MAX_STRING], szValue[MAX_STRING];
|
|
DWORD cbName, cbValue;
|
|
for (;;)
|
|
{
|
|
cbName = sizeof(szName);
|
|
cbValue = sizeof(szValue);
|
|
LONG Error = RegEnumValueW(hKey, 0, szName, &cbName,
|
|
NULL, NULL, (LPBYTE)szValue, &cbValue);
|
|
if (Error != ERROR_SUCCESS)
|
|
break;
|
|
|
|
RegDeleteValueW(hKey, szName);
|
|
}
|
|
|
|
// set values
|
|
size_t Count = g_Items.size();
|
|
for (size_t i = 0; i < Count; ++i)
|
|
{
|
|
DWORD cbData = (g_Items[i].m_Substitute.size() + 1) * sizeof(WCHAR);
|
|
RegSetValueExW(hKey, g_Items[i].m_Name.c_str(), 0,
|
|
REG_SZ, (LPBYTE)g_Items[i].m_Substitute.c_str(), cbData);
|
|
}
|
|
|
|
// close now
|
|
RegCloseKey(hKey);
|
|
|
|
g_bModified = FALSE;
|
|
g_bNeedsReboot = TRUE;
|
|
return TRUE;
|
|
}
|
|
|
|
LPWSTR SkipSpace(LPCWSTR pch)
|
|
{
|
|
while (*pch && wcschr(L" \t\r\n", *pch) != NULL)
|
|
{
|
|
++pch;
|
|
}
|
|
return const_cast<LPWSTR>(pch);
|
|
}
|
|
|
|
LPWSTR SkipQuoted(LPWSTR pch)
|
|
{
|
|
++pch; // L'"'
|
|
while (*pch)
|
|
{
|
|
if (*pch == L'"')
|
|
{
|
|
++pch;
|
|
break;
|
|
}
|
|
if (*pch == L'\\')
|
|
{
|
|
++pch;
|
|
}
|
|
++pch;
|
|
}
|
|
return pch;
|
|
}
|
|
|
|
void UnescapeHex(const STRING& str, size_t& i, STRING& Ret, BOOL Unicode)
|
|
{
|
|
STRING Num;
|
|
|
|
// hexadecimal
|
|
if (iswxdigit(str[i]))
|
|
{
|
|
Num += str[i];
|
|
++i;
|
|
if (iswxdigit(str[i]))
|
|
{
|
|
Num += str[i];
|
|
++i;
|
|
if (Unicode)
|
|
{
|
|
if (iswxdigit(str[i]))
|
|
{
|
|
Num += str[i];
|
|
++i;
|
|
if (iswxdigit(str[i]))
|
|
{
|
|
Num += str[i];
|
|
++i;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!Num.empty())
|
|
{
|
|
Ret += (WCHAR)wcstoul(&Num[0], NULL, 16);
|
|
}
|
|
}
|
|
|
|
void UnescapeOther(const STRING& str, size_t& i, STRING& Ret)
|
|
{
|
|
STRING Num;
|
|
|
|
// check octal
|
|
if (L'0' <= str[i] && str[i] < L'8')
|
|
{
|
|
Num += str[i];
|
|
++i;
|
|
if (L'0' <= str[i] && str[i] < L'8')
|
|
{
|
|
Num += str[i];
|
|
++i;
|
|
if (L'0' <= str[i] && str[i] < L'8')
|
|
{
|
|
Num += str[i];
|
|
++i;
|
|
}
|
|
}
|
|
}
|
|
if (Num.empty())
|
|
{
|
|
Ret += str[i];
|
|
++i;
|
|
}
|
|
else
|
|
{
|
|
// octal
|
|
Ret += (WCHAR)wcstoul(&Num[0], NULL, 8);
|
|
}
|
|
}
|
|
|
|
// process escape sequence
|
|
void UnescapeChar(const STRING& str, size_t& i, STRING& Ret)
|
|
{
|
|
if (str[i] != L'\\')
|
|
{
|
|
Ret += str[i];
|
|
++i;
|
|
return;
|
|
}
|
|
|
|
++i;
|
|
switch (str[i])
|
|
{
|
|
case L'a': Ret += L'\a'; ++i; break;
|
|
case L'b': Ret += L'\b'; ++i; break;
|
|
case L'f': Ret += L'\f'; ++i; break;
|
|
case L'n': Ret += L'\n'; ++i; break;
|
|
case L'r': Ret += L'\r'; ++i; break;
|
|
case L't': Ret += L'\t'; ++i; break;
|
|
case L'v': Ret += L'\v'; ++i; break;
|
|
case L'x':
|
|
// hexidemical
|
|
++i;
|
|
UnescapeHex(str, i, Ret, FALSE);
|
|
break;
|
|
case L'u':
|
|
// Unicode hexidemical
|
|
++i;
|
|
UnescapeHex(str, i, Ret, TRUE);
|
|
break;
|
|
default:
|
|
// other case
|
|
UnescapeOther(str, i, Ret);
|
|
break;
|
|
}
|
|
}
|
|
|
|
STRING Unquote(const STRING& str)
|
|
{
|
|
if (str[0] != L'"')
|
|
return str;
|
|
|
|
STRING Ret;
|
|
size_t i = 1;
|
|
while (i < str.size())
|
|
{
|
|
if (str[i] == L'"' || str[i] == UNICODE_NULL)
|
|
break;
|
|
|
|
UnescapeChar(str, i, Ret);
|
|
}
|
|
return Ret;
|
|
}
|
|
|
|
BOOL DoParseFile(LPVOID pvContents, DWORD dwSize)
|
|
{
|
|
ITEMVECTOR Items;
|
|
|
|
LPWSTR pch, pchSep, pchStart = (LPWSTR)pvContents;
|
|
|
|
pchStart[dwSize / sizeof(WCHAR)] = UNICODE_NULL;
|
|
|
|
// check header
|
|
const DWORD cbHeader = lstrlenW(g_pszFileHeader) * sizeof(WCHAR);
|
|
if (memcmp(pchStart, g_pszFileHeader, cbHeader) != 0)
|
|
return FALSE;
|
|
|
|
pchStart += cbHeader / sizeof(WCHAR);
|
|
|
|
// find the key
|
|
WCHAR szKey[MAX_STRING];
|
|
wsprintfW(szKey, L"[HKEY_LOCAL_MACHINE\\%s]", g_pszKey);
|
|
pch = wcsstr(pchStart, szKey);
|
|
if (pch == NULL)
|
|
return FALSE;
|
|
|
|
pchStart = pch + lstrlenW(szKey);
|
|
|
|
for (;;)
|
|
{
|
|
pchStart = SkipSpace(pchStart);
|
|
if (*pchStart == UNICODE_NULL || *pchStart == L'[')
|
|
break;
|
|
|
|
pch = wcschr(pchStart, L'\n');
|
|
if (pch)
|
|
*pch = UNICODE_NULL;
|
|
|
|
pchSep = SkipQuoted(pchStart);
|
|
if (*pchSep == L'=')
|
|
{
|
|
*pchSep = UNICODE_NULL;
|
|
|
|
STRING key = pchStart;
|
|
trim(key);
|
|
key = Unquote(key);
|
|
|
|
STRING value = pchSep + 1;
|
|
trim(value);
|
|
value = Unquote(value);
|
|
|
|
BYTE CharSet1 = DEFAULT_CHARSET, CharSet2 = DEFAULT_CHARSET;
|
|
|
|
size_t pos;
|
|
pos = key.find(L',');
|
|
if (pos != STRING::npos)
|
|
{
|
|
CharSet1 = (BYTE)_wtoi(&key[pos + 1]);
|
|
key.resize(pos);
|
|
trim(key);
|
|
}
|
|
pos = value.find(L',');
|
|
if (pos != STRING::npos)
|
|
{
|
|
CharSet2 = (BYTE)_wtoi(&value[pos + 1]);
|
|
value.resize(pos);
|
|
trim(value);
|
|
}
|
|
|
|
ITEM Item(key, value, CharSet1, CharSet2);
|
|
Items.push_back(Item);
|
|
}
|
|
|
|
if (pch == NULL)
|
|
break;
|
|
|
|
pchStart = pch + 1;
|
|
}
|
|
|
|
g_Items = Items;
|
|
g_bModified = TRUE;
|
|
|
|
LV_AddItems(g_hListView);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL DoImport(HWND hwnd, LPCWSTR pszFile)
|
|
{
|
|
HANDLE hFile = CreateFileW(pszFile, GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_DELETE, NULL,
|
|
OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
return FALSE;
|
|
|
|
BOOL bSuccess = FALSE;
|
|
DWORD dwSize = GetFileSize(hFile, NULL);
|
|
if (dwSize != 0xFFFFFFFF)
|
|
{
|
|
std::vector<BYTE> Contents(dwSize + 2);
|
|
DWORD cbRead;
|
|
if (ReadFile(hFile, &Contents[0], dwSize, &cbRead, NULL) &&
|
|
cbRead == dwSize)
|
|
{
|
|
/* check BOM */
|
|
if (memcmp(&Contents[0], "\xFF\xFE", 2) == 0)
|
|
{
|
|
bSuccess = DoParseFile(&Contents[2], dwSize - 2);
|
|
}
|
|
else
|
|
{
|
|
bSuccess = DoParseFile(&Contents[0], dwSize);
|
|
}
|
|
}
|
|
}
|
|
CloseHandle(hFile);
|
|
|
|
return bSuccess;
|
|
}
|
|
|
|
STRING Escape(const STRING& str)
|
|
{
|
|
STRING Ret;
|
|
for (size_t i = 0; i < str.size(); ++i)
|
|
{
|
|
switch (str[i])
|
|
{
|
|
case L'"': case L'\\':
|
|
Ret += L'\\';
|
|
Ret += str[i];
|
|
break;
|
|
default:
|
|
Ret += str[i];
|
|
}
|
|
}
|
|
return Ret;
|
|
}
|
|
|
|
BOOL DoExport(HWND hwnd, LPCWSTR pszFile)
|
|
{
|
|
HANDLE hFile = CreateFileW(pszFile, GENERIC_WRITE, FILE_SHARE_READ,
|
|
NULL, CREATE_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, NULL);
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
return FALSE;
|
|
|
|
BOOL bSuccess;
|
|
DWORD dwSize, cbWritten;
|
|
WCHAR szCharSet1[MAX_STRING], szCharSet2[MAX_STRING];
|
|
WCHAR szLine[MAX_STRING * 2 + 4];
|
|
|
|
/* write header */
|
|
dwSize = lstrlenW(g_pszFileHeader) * sizeof(WCHAR);
|
|
bSuccess =
|
|
WriteFile(hFile, "\xFF\xFE", 2, &cbWritten, NULL) &&
|
|
WriteFile(hFile, g_pszFileHeader, dwSize, &cbWritten, NULL);
|
|
if (bSuccess)
|
|
{
|
|
wsprintfW(szLine, L"\r\n\r\n[HKEY_LOCAL_MACHINE\\%s]\r\n", g_pszKey);
|
|
dwSize = lstrlenW(szLine) * sizeof(WCHAR);
|
|
bSuccess = WriteFile(hFile, szLine, dwSize, &cbWritten, NULL);
|
|
}
|
|
if (bSuccess)
|
|
{
|
|
size_t i, Count = g_Items.size();
|
|
for (i = 0; i < Count; ++i)
|
|
{
|
|
if (g_Items[i].m_CharSet1 != DEFAULT_CHARSET)
|
|
wsprintfW(szCharSet1, L",%u", g_Items[i].m_CharSet1);
|
|
else
|
|
szCharSet1[0] = UNICODE_NULL;
|
|
|
|
if (g_Items[i].m_CharSet2 != DEFAULT_CHARSET)
|
|
wsprintfW(szCharSet2, L",%u", g_Items[i].m_CharSet2);
|
|
else
|
|
szCharSet2[0] = UNICODE_NULL;
|
|
|
|
STRING Name = Escape(g_Items[i].m_Name);
|
|
STRING Substitute = Escape(g_Items[i].m_Substitute);
|
|
wsprintfW(szLine, L"\"%s%s\"=\"%s%s\"\r\n",
|
|
Name.c_str(), szCharSet1,
|
|
Substitute.c_str(), szCharSet2);
|
|
|
|
dwSize = lstrlenW(szLine) * sizeof(WCHAR);
|
|
if (!WriteFile(hFile, szLine, dwSize, &cbWritten, NULL))
|
|
{
|
|
bSuccess = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
WriteFile(hFile, L"\r\n", 2 * sizeof(WCHAR), &cbWritten, NULL);
|
|
}
|
|
CloseHandle(hFile);
|
|
|
|
if (!bSuccess)
|
|
{
|
|
DeleteFileW(pszFile);
|
|
}
|
|
|
|
return bSuccess;
|
|
}
|
|
|
|
void MakeFilter(LPWSTR pszFilter)
|
|
{
|
|
while (*pszFilter)
|
|
{
|
|
if (*pszFilter == L'|')
|
|
*pszFilter = 0;
|
|
|
|
++pszFilter;
|
|
}
|
|
}
|
|
|
|
void MainWnd_OnImport(HWND hwnd)
|
|
{
|
|
OPENFILENAMEW ofn = {0};
|
|
WCHAR szFile[MAX_PATH] = L"";
|
|
WCHAR szImportTitle[MAX_STRING];
|
|
WCHAR szCannotImport[MAX_STRING];
|
|
WCHAR szImportFilter[MAX_STRING];
|
|
LoadStringW(g_hInstance, IDS_IMPORT, szImportTitle, _countof(szImportTitle));
|
|
LoadStringW(g_hInstance, IDS_CANTIMPORT, szCannotImport, _countof(szCannotImport));
|
|
LoadStringW(g_hInstance, IDS_INPFILTER, szImportFilter, _countof(szImportFilter));
|
|
MakeFilter(szImportFilter);
|
|
|
|
ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
|
|
ofn.hwndOwner = hwnd;
|
|
ofn.lpstrFilter = szImportFilter;
|
|
ofn.lpstrFile = szFile;
|
|
ofn.nMaxFile = _countof(szFile);
|
|
ofn.lpstrTitle = szImportTitle;
|
|
ofn.Flags = OFN_DONTADDTORECENT | OFN_ENABLESIZING |
|
|
OFN_EXPLORER | OFN_FILEMUSTEXIST |
|
|
OFN_HIDEREADONLY | OFN_LONGNAMES |
|
|
OFN_PATHMUSTEXIST;
|
|
ofn.lpstrDefExt = L"reg";
|
|
if (GetOpenFileNameW(&ofn))
|
|
{
|
|
if (!DoImport(hwnd, szFile))
|
|
{
|
|
MessageBoxW(hwnd, szCannotImport, g_szTitle, MB_ICONERROR);
|
|
}
|
|
}
|
|
}
|
|
|
|
void MainWnd_OnExport(HWND hwnd)
|
|
{
|
|
OPENFILENAMEW ofn = {0};
|
|
WCHAR szFile[MAX_PATH] = L"";
|
|
WCHAR szExportTitle[MAX_STRING];
|
|
WCHAR szCannotExport[MAX_STRING];
|
|
WCHAR szExportFilter[MAX_STRING];
|
|
LoadStringW(g_hInstance, IDS_EXPORT, szExportTitle, _countof(szExportTitle));
|
|
LoadStringW(g_hInstance, IDS_CANTEXPORT, szCannotExport, _countof(szCannotExport));
|
|
LoadStringW(g_hInstance, IDS_OUTFILTER, szExportFilter, _countof(szExportFilter));
|
|
MakeFilter(szExportFilter);
|
|
|
|
ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
|
|
ofn.hwndOwner = hwnd;
|
|
ofn.lpstrFilter = szExportFilter;
|
|
ofn.lpstrFile = szFile;
|
|
ofn.nMaxFile = _countof(szFile);
|
|
ofn.lpstrTitle = szExportTitle;
|
|
ofn.Flags = OFN_DONTADDTORECENT | OFN_ENABLESIZING |
|
|
OFN_EXPLORER | OFN_HIDEREADONLY | OFN_LONGNAMES |
|
|
OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT;
|
|
ofn.lpstrDefExt = L"reg";
|
|
if (GetSaveFileNameW(&ofn))
|
|
{
|
|
if (!DoExport(hwnd, szFile))
|
|
{
|
|
MessageBoxW(hwnd, szCannotExport, g_szTitle, MB_ICONERROR);
|
|
}
|
|
}
|
|
}
|
|
|
|
void MainWnd_OnReload(HWND hwnd)
|
|
{
|
|
DoLoad();
|
|
}
|
|
|
|
void MainWnd_OnEdit(HWND hwnd)
|
|
{
|
|
LV_OnDblClk(g_hListView);
|
|
}
|
|
|
|
void MainWnd_OnDelete(HWND hwnd)
|
|
{
|
|
LV_OnDelete(g_hListView);
|
|
}
|
|
|
|
void MainWnd_OnOpenRegKey(HWND hwnd)
|
|
{
|
|
static const WCHAR s_szRegeditKey[] =
|
|
L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Applets\\Regedit";
|
|
WCHAR sz[MAX_STRING];
|
|
|
|
// open regedit key
|
|
HKEY hKey = NULL;
|
|
LSTATUS Result = RegCreateKeyExW(HKEY_CURRENT_USER, s_szRegeditKey, 0,
|
|
NULL, 0, KEY_WRITE, NULL, &hKey, NULL);
|
|
if (Result != ERROR_SUCCESS)
|
|
{
|
|
LoadStringW(g_hInstance, IDS_CANTOPENKEY, sz, _countof(sz));
|
|
MessageBoxW(hwnd, sz, NULL, MB_ICONERROR);
|
|
return;
|
|
}
|
|
|
|
// set LastKey value
|
|
wsprintfW(sz, L"HKEY_LOCAL_MACHINE\\%s", g_pszKey);
|
|
DWORD dwSize = sizeof(sz);
|
|
Result = RegSetValueExW(hKey, L"LastKey", 0, REG_SZ,
|
|
(LPBYTE)sz, dwSize);
|
|
|
|
// close now
|
|
RegCloseKey(hKey);
|
|
|
|
if (Result != ERROR_SUCCESS)
|
|
{
|
|
LoadStringW(g_hInstance, IDS_CANTOPENKEY, sz, _countof(sz));
|
|
MessageBoxW(hwnd, sz, NULL, MB_ICONERROR);
|
|
return;
|
|
}
|
|
|
|
// open by regedit
|
|
ShellExecuteW(hwnd, NULL, L"regedit.exe", NULL, NULL, SW_SHOWNORMAL);
|
|
}
|
|
|
|
void MainWnd_OnAbout(HWND hwnd)
|
|
{
|
|
WCHAR szAbout[MAX_PATH];
|
|
LoadStringW(g_hInstance, IDS_ABOUT, szAbout, _countof(szAbout));
|
|
|
|
MSGBOXPARAMS Params;
|
|
ZeroMemory(&Params, sizeof(Params));
|
|
Params.cbSize = sizeof(Params);
|
|
Params.hwndOwner = hwnd;
|
|
Params.hInstance = g_hInstance;
|
|
Params.lpszText = szAbout;
|
|
Params.lpszCaption = g_szTitle;
|
|
Params.dwStyle = MB_OK | MB_USERICON;
|
|
Params.lpszIcon = MAKEINTRESOURCEW(1);
|
|
Params.dwLanguageId = LANG_USER_DEFAULT;
|
|
MessageBoxIndirectW(&Params);
|
|
}
|
|
|
|
void MainWnd_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
|
|
{
|
|
switch (id)
|
|
{
|
|
case ID_NEW:
|
|
MainWnd_OnNew(hwnd);
|
|
break;
|
|
case ID_EDIT:
|
|
MainWnd_OnEdit(hwnd);
|
|
break;
|
|
case ID_EXIT:
|
|
PostMessage(hwnd, WM_CLOSE, 0, 0);
|
|
break;
|
|
case ID_RELOAD:
|
|
MainWnd_OnReload(hwnd);
|
|
break;
|
|
case ID_UPDATE_REGISTRY:
|
|
MainWnd_OnUpdateRegistry(hwnd);
|
|
break;
|
|
case ID_DELETE:
|
|
MainWnd_OnDelete(hwnd);
|
|
break;
|
|
case ID_IMPORT:
|
|
MainWnd_OnImport(hwnd);
|
|
break;
|
|
case ID_EXPORT:
|
|
MainWnd_OnExport(hwnd);
|
|
break;
|
|
case ID_OPEN_REGKEY:
|
|
MainWnd_OnOpenRegKey(hwnd);
|
|
break;
|
|
case ID_ABOUT:
|
|
MainWnd_OnAbout(hwnd);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void MainWnd_OnDestroy(HWND hwnd)
|
|
{
|
|
PostQuitMessage(0);
|
|
}
|
|
|
|
void MainWnd_OnSize(HWND hwnd, UINT state, int cx, int cy)
|
|
{
|
|
MoveWindow(g_hListView, 0, 0, cx, cy, TRUE);
|
|
}
|
|
|
|
void MainWnd_OnDrawItem(HWND hwnd, const DRAWITEMSTRUCT *lpDrawItem)
|
|
{
|
|
if (lpDrawItem->CtlType != ODT_LISTVIEW)
|
|
return;
|
|
|
|
HDC hDC = lpDrawItem->hDC;
|
|
SetBkMode(hDC, TRANSPARENT);
|
|
|
|
INT iColumn = 0, x, cx;
|
|
RECT rcItem, rcSubItem, rcText;
|
|
STRING Str;
|
|
|
|
x = -GetScrollPos(g_hListView, SB_HORZ);
|
|
|
|
rcItem = lpDrawItem->rcItem;
|
|
if (lpDrawItem->itemState & ODS_SELECTED)
|
|
{
|
|
FillRect(hDC, &rcItem, (HBRUSH)(COLOR_HIGHLIGHT + 1));
|
|
SetTextColor(hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
|
|
}
|
|
else
|
|
{
|
|
FillRect(hDC, &rcItem, (HBRUSH)(COLOR_WINDOW + 1));
|
|
SetTextColor(hDC, GetSysColor(COLOR_WINDOWTEXT));
|
|
}
|
|
|
|
cx = ListView_GetColumnWidth(g_hListView, iColumn);
|
|
rcSubItem = rcItem;
|
|
rcSubItem.left = x;
|
|
rcSubItem.right = x + cx;
|
|
|
|
WCHAR sz[MAX_STRING];
|
|
|
|
rcText = rcSubItem;
|
|
InflateRect(&rcText, -1, -1);
|
|
Str = g_Items[lpDrawItem->itemID].m_Name;
|
|
BYTE CharSet1 = g_Items[lpDrawItem->itemID].m_CharSet1;
|
|
if (CharSet1 != DEFAULT_CHARSET)
|
|
wsprintfW(sz, L"%s,%u", Str.c_str(), CharSet1);
|
|
else
|
|
wsprintfW(sz, L"%s", Str.c_str());
|
|
|
|
DrawTextW(hDC, sz, lstrlenW(sz), &rcText,
|
|
DT_SINGLELINE | DT_LEFT | DT_VCENTER | DT_END_ELLIPSIS |
|
|
DT_NOPREFIX);
|
|
|
|
x += cx;
|
|
++iColumn;
|
|
|
|
cx = ListView_GetColumnWidth(g_hListView, iColumn);
|
|
rcSubItem = rcItem;
|
|
rcSubItem.left = x;
|
|
rcSubItem.right = x + cx;
|
|
|
|
rcText = rcSubItem;
|
|
InflateRect(&rcText, -1, -1);
|
|
Str = g_Items[lpDrawItem->itemID].m_Substitute;
|
|
BYTE CharSet2 = g_Items[lpDrawItem->itemID].m_CharSet2;
|
|
if (CharSet2 != DEFAULT_CHARSET)
|
|
wsprintfW(sz, L"%s,%u", Str.c_str(), CharSet2);
|
|
else
|
|
wsprintfW(sz, L"%s", Str.c_str());
|
|
|
|
DrawTextW(hDC, sz, lstrlenW(sz), &rcText,
|
|
DT_SINGLELINE | DT_LEFT | DT_VCENTER | DT_END_ELLIPSIS |
|
|
DT_NOPREFIX);
|
|
}
|
|
|
|
void MainWnd_OnMeasureItem(HWND hwnd, MEASUREITEMSTRUCT *lpMeasureItem)
|
|
{
|
|
if (lpMeasureItem->CtlType != ODT_LISTVIEW)
|
|
return;
|
|
|
|
TEXTMETRIC tm;
|
|
HDC hDC = GetDC(hwnd);
|
|
GetTextMetrics(hDC, &tm);
|
|
ReleaseDC(hwnd, hDC);
|
|
|
|
lpMeasureItem->itemHeight = tm.tmHeight * 4 / 3;
|
|
}
|
|
|
|
LRESULT MainWnd_OnNotify(HWND hwnd, int idFrom, NMHDR *pnmhdr)
|
|
{
|
|
NM_LISTVIEW *pNMLV = (NM_LISTVIEW *)pnmhdr;
|
|
LV_KEYDOWN *pLVKD = (LV_KEYDOWN *)pnmhdr;
|
|
|
|
switch (pnmhdr->code)
|
|
{
|
|
case LVN_COLUMNCLICK:
|
|
if (pNMLV->iSubItem == g_iSortColumn)
|
|
DoSort(pNMLV->iSubItem, !g_bSortAscendant);
|
|
else
|
|
DoSort(pNMLV->iSubItem, TRUE);
|
|
break;
|
|
case NM_DBLCLK:
|
|
LV_OnDblClk(g_hListView);
|
|
break;
|
|
case LVN_KEYDOWN:
|
|
if (pLVKD->wVKey == VK_RETURN) // [Enter] key
|
|
{
|
|
LV_OnDblClk(g_hListView);
|
|
}
|
|
if (pLVKD->wVKey == VK_DELETE) // [Del] key
|
|
{
|
|
LV_OnDelete(g_hListView);
|
|
}
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
LRESULT MainWnd_OnContextMenu(HWND hwnd, HWND hwndContext, UINT xPos, UINT yPos)
|
|
{
|
|
POINT pt = {(INT)xPos, (INT)yPos};
|
|
ScreenToClient(g_hListView, &pt);
|
|
SendMessageW(g_hListView, WM_LBUTTONDOWN, 0, MAKELPARAM(pt.x, pt.y));
|
|
|
|
HMENU hMenu = LoadMenu(g_hInstance, MAKEINTRESOURCE(2));
|
|
if (hMenu == NULL)
|
|
return 0;
|
|
|
|
HMENU hSubMenu = GetSubMenu(hMenu, 0);
|
|
if (hSubMenu == NULL)
|
|
return 0;
|
|
|
|
SetForegroundWindow(hwnd);
|
|
TrackPopupMenu(hSubMenu, TPM_LEFTALIGN | TPM_RIGHTBUTTON,
|
|
xPos, yPos, 0, g_hMainWnd, NULL);
|
|
PostMessage(g_hMainWnd, WM_NULL, 0, 0);
|
|
return 0;
|
|
}
|
|
|
|
void MainWnd_OnActivate(HWND hwnd, UINT state, HWND hwndActDeact, BOOL fMinimized)
|
|
{
|
|
if (state != WA_INACTIVE)
|
|
{
|
|
SetFocus(g_hListView);
|
|
}
|
|
}
|
|
|
|
BOOL EnableProcessPrivileges(LPCWSTR lpPrivilegeName, BOOL bEnable = TRUE)
|
|
{
|
|
HANDLE hToken;
|
|
LUID luid;
|
|
TOKEN_PRIVILEGES tokenPrivileges;
|
|
BOOL Ret;
|
|
|
|
Ret = ::OpenProcessToken(::GetCurrentProcess(),
|
|
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
|
|
&hToken);
|
|
if (!Ret)
|
|
return Ret; // failure
|
|
|
|
Ret = ::LookupPrivilegeValueW(NULL, lpPrivilegeName, &luid);
|
|
if (Ret)
|
|
{
|
|
tokenPrivileges.PrivilegeCount = 1;
|
|
tokenPrivileges.Privileges[0].Luid = luid;
|
|
tokenPrivileges.Privileges[0].Attributes = bEnable ? SE_PRIVILEGE_ENABLED : 0;
|
|
|
|
Ret = ::AdjustTokenPrivileges(hToken, FALSE, &tokenPrivileges, 0, 0, 0);
|
|
}
|
|
|
|
::CloseHandle(hToken);
|
|
return Ret;
|
|
}
|
|
|
|
void MainWnd_OnClose(HWND hwnd)
|
|
{
|
|
if (!g_bNeedsReboot && !g_bModified)
|
|
{
|
|
DestroyWindow(hwnd);
|
|
return;
|
|
}
|
|
|
|
if (g_bModified)
|
|
{
|
|
WCHAR szUpdateNow[MAX_STRING];
|
|
LoadStringW(g_hInstance, IDS_QUERYUPDATE, szUpdateNow, _countof(szUpdateNow));
|
|
INT id = MessageBoxW(hwnd, szUpdateNow, g_szTitle,
|
|
MB_ICONINFORMATION | MB_YESNOCANCEL);
|
|
switch (id)
|
|
{
|
|
case IDYES:
|
|
MainWnd_OnUpdateRegistry(hwnd);
|
|
break;
|
|
case IDNO:
|
|
break;
|
|
case IDCANCEL:
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (g_bNeedsReboot)
|
|
{
|
|
WCHAR szRebootNow[MAX_STRING];
|
|
LoadStringW(g_hInstance, IDS_REBOOTNOW, szRebootNow, _countof(szRebootNow));
|
|
INT id = MessageBoxW(hwnd, szRebootNow, g_szTitle,
|
|
MB_ICONINFORMATION | MB_YESNOCANCEL);
|
|
switch (id)
|
|
{
|
|
case IDYES:
|
|
EnableProcessPrivileges(SE_SHUTDOWN_NAME, TRUE);
|
|
::ExitWindowsEx(EWX_REBOOT | EWX_FORCE, 0);
|
|
break;
|
|
case IDNO:
|
|
break;
|
|
case IDCANCEL:
|
|
return;
|
|
}
|
|
}
|
|
|
|
::DestroyWindow(hwnd);
|
|
}
|
|
|
|
LRESULT CALLBACK
|
|
WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (uMsg)
|
|
{
|
|
HANDLE_MSG(hwnd, WM_CREATE, MainWnd_OnCreate);
|
|
HANDLE_MSG(hwnd, WM_COMMAND, MainWnd_OnCommand);
|
|
HANDLE_MSG(hwnd, WM_DESTROY, MainWnd_OnDestroy);
|
|
HANDLE_MSG(hwnd, WM_SIZE, MainWnd_OnSize);
|
|
HANDLE_MSG(hwnd, WM_DRAWITEM, MainWnd_OnDrawItem);
|
|
HANDLE_MSG(hwnd, WM_MEASUREITEM, MainWnd_OnMeasureItem);
|
|
HANDLE_MSG(hwnd, WM_NOTIFY, MainWnd_OnNotify);
|
|
HANDLE_MSG(hwnd, WM_CONTEXTMENU, MainWnd_OnContextMenu);
|
|
HANDLE_MSG(hwnd, WM_ACTIVATE, MainWnd_OnActivate);
|
|
HANDLE_MSG(hwnd, WM_CLOSE, MainWnd_OnClose);
|
|
default:
|
|
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
|
}
|
|
}
|
|
|
|
INT WINAPI wWinMain(
|
|
HINSTANCE hInstance,
|
|
HINSTANCE hPrevInstance,
|
|
LPWSTR lpCmdLine,
|
|
INT nCmdShow)
|
|
{
|
|
g_hInstance = hInstance;
|
|
InitCommonControls();
|
|
|
|
HACCEL hAccel = LoadAcceleratorsW(hInstance, MAKEINTRESOURCEW(1));
|
|
|
|
LoadStringW(hInstance, IDS_TITLE, g_szTitle, _countof(g_szTitle));
|
|
LoadStringW(hInstance, IDS_FONTNAME, g_szNameHead, _countof(g_szNameHead));
|
|
LoadStringW(hInstance, IDS_SUBSTITUTE, g_szSubstituteHead, _countof(g_szSubstituteHead));
|
|
|
|
WNDCLASSW wc = {0};
|
|
wc.style = 0;
|
|
wc.lpfnWndProc = WindowProc;
|
|
wc.hInstance = hInstance;
|
|
g_hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(1));
|
|
wc.hIcon = g_hIcon;
|
|
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
|
|
wc.lpszMenuName = MAKEINTRESOURCEW(1);
|
|
wc.lpszClassName = g_pszClassName;
|
|
if (!RegisterClassW(&wc))
|
|
{
|
|
MessageBoxA(NULL, "ERROR: RegisterClass failed.", NULL, MB_ICONERROR);
|
|
return 1;
|
|
}
|
|
|
|
const DWORD dwStyle = WS_OVERLAPPEDWINDOW;
|
|
INT Width = NAME_COLUMN_WIDTH + SUB_COLUMN_WIDTH +
|
|
GetSystemMetrics(SM_CXVSCROLL) +
|
|
GetSystemMetrics(SM_CXSIZEFRAME);
|
|
INT Height = 320;
|
|
|
|
RECT Rect = { 0, 0, Width, Height };
|
|
AdjustWindowRect(&Rect, dwStyle, TRUE);
|
|
Width = Rect.right - Rect.left;
|
|
Height = Rect.bottom - Rect.top;
|
|
|
|
g_hMainWnd = CreateWindowW(g_pszClassName, g_szTitle, dwStyle,
|
|
CW_USEDEFAULT, CW_USEDEFAULT, Width, Height,
|
|
NULL, NULL, hInstance, NULL);
|
|
if (g_hMainWnd == NULL)
|
|
{
|
|
MessageBoxA(NULL, "ERROR: CreateWindow failed.", NULL, MB_ICONERROR);
|
|
return 2;
|
|
}
|
|
|
|
ShowWindow(g_hMainWnd, nCmdShow);
|
|
UpdateWindow(g_hMainWnd);
|
|
|
|
MSG msg;
|
|
while (GetMessage(&msg, NULL, 0, 0))
|
|
{
|
|
if (TranslateAccelerator(g_hMainWnd, hAccel, &msg))
|
|
continue;
|
|
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
|
|
return (INT)msg.wParam;
|
|
}
|