160 lines
5.2 KiB
C++
160 lines
5.2 KiB
C++
// Scintilla source code edit control
|
|
/** @file HanjaDic.cxx
|
|
** Korean Hanja Dictionary
|
|
** Convert between Korean Hanja and Hangul by COM interface.
|
|
**/
|
|
// Copyright 2015 by Neil Hodgson <neilh@scintilla.org>
|
|
// The License.txt file describes the conditions under which this software may be distributed.
|
|
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <memory>
|
|
|
|
#define WIN32_LEAN_AND_MEAN 1
|
|
#include <windows.h>
|
|
#include <ole2.h>
|
|
|
|
#include "WinTypes.h"
|
|
#include "HanjaDic.h"
|
|
|
|
namespace Scintilla::Internal::HanjaDict {
|
|
|
|
interface IRadical;
|
|
interface IHanja;
|
|
interface IStrokes;
|
|
|
|
enum HANJA_TYPE { HANJA_UNKNOWN = 0, HANJA_K0 = 1, HANJA_K1 = 2, HANJA_OTHER = 3 };
|
|
|
|
interface IHanjaDic : IUnknown {
|
|
STDMETHOD(OpenMainDic)();
|
|
STDMETHOD(CloseMainDic)();
|
|
STDMETHOD(GetHanjaWords)(BSTR bstrHangul, SAFEARRAY* ppsaHanja, VARIANT_BOOL* pfFound);
|
|
STDMETHOD(GetHanjaChars)(unsigned short wchHangul, BSTR* pbstrHanjaChars, VARIANT_BOOL* pfFound);
|
|
STDMETHOD(HanjaToHangul)(BSTR bstrHanja, BSTR* pbstrHangul);
|
|
STDMETHOD(GetHanjaType)(unsigned short wchHanja, HANJA_TYPE* pHanjaType);
|
|
STDMETHOD(GetHanjaSense)(unsigned short wchHanja, BSTR* pbstrSense);
|
|
STDMETHOD(GetRadicalID)(short SeqNumOfRadical, short* pRadicalID, unsigned short* pwchRadical);
|
|
STDMETHOD(GetRadical)(short nRadicalID, IRadical** ppIRadical);
|
|
STDMETHOD(RadicalIDToHanja)(short nRadicalID, unsigned short* pwchRadical);
|
|
STDMETHOD(GetHanja)(unsigned short wchHanja, IHanja** ppIHanja);
|
|
STDMETHOD(GetStrokes)(short nStrokes, IStrokes** ppIStrokes);
|
|
STDMETHOD(OpenDefaultCustomDic)();
|
|
STDMETHOD(OpenCustomDic)(BSTR bstrPath, long* plUdr);
|
|
STDMETHOD(CloseDefaultCustomDic)();
|
|
STDMETHOD(CloseCustomDic)(long lUdr);
|
|
STDMETHOD(CloseAllCustomDics)();
|
|
STDMETHOD(GetDefaultCustomHanjaWords)(BSTR bstrHangul, SAFEARRAY** ppsaHanja, VARIANT_BOOL* pfFound);
|
|
STDMETHOD(GetCustomHanjaWords)(long lUdr, BSTR bstrHangul, SAFEARRAY** ppsaHanja, VARIANT_BOOL* pfFound);
|
|
STDMETHOD(PutDefaultCustomHanjaWord)(BSTR bstrHangul, BSTR bstrHanja);
|
|
STDMETHOD(PutCustomHanjaWord)(long lUdr, BSTR bstrHangul, BSTR bstrHanja);
|
|
STDMETHOD(MaxNumOfRadicals)(short* pVal);
|
|
STDMETHOD(MaxNumOfStrokes)(short* pVal);
|
|
STDMETHOD(DefaultCustomDic)(long* pVal);
|
|
STDMETHOD(DefaultCustomDic)(long pVal);
|
|
STDMETHOD(MaxHanjaType)(HANJA_TYPE* pHanjaType);
|
|
STDMETHOD(MaxHanjaType)(HANJA_TYPE pHanjaType);
|
|
};
|
|
|
|
extern "C" const GUID __declspec(selectany) IID_IHanjaDic =
|
|
{ 0xad75f3ac, 0x18cd, 0x48c6, { 0xa2, 0x7d, 0xf1, 0xe9, 0xa7, 0xdc, 0xe4, 0x32 } };
|
|
|
|
class ScopedBSTR {
|
|
BSTR bstr = nullptr;
|
|
public:
|
|
ScopedBSTR() noexcept = default;
|
|
explicit ScopedBSTR(const OLECHAR *psz) noexcept :
|
|
bstr(SysAllocString(psz)) {
|
|
}
|
|
explicit ScopedBSTR(OLECHAR character) noexcept :
|
|
bstr(SysAllocStringLen(&character, 1)) {
|
|
}
|
|
// Deleted so ScopedBSTR objects can not be copied. Moves are OK.
|
|
ScopedBSTR(const ScopedBSTR &) = delete;
|
|
ScopedBSTR &operator=(const ScopedBSTR &) = delete;
|
|
// Moves are OK.
|
|
ScopedBSTR(ScopedBSTR &&) = default;
|
|
ScopedBSTR &operator=(ScopedBSTR &&) = default;
|
|
~ScopedBSTR() {
|
|
SysFreeString(bstr);
|
|
}
|
|
|
|
BSTR get() const noexcept {
|
|
return bstr;
|
|
}
|
|
void reset(BSTR value=nullptr) noexcept {
|
|
// https://en.cppreference.com/w/cpp/memory/unique_ptr/reset
|
|
BSTR const old = bstr;
|
|
bstr = value;
|
|
SysFreeString(old);
|
|
}
|
|
};
|
|
|
|
class HanjaDic {
|
|
std::unique_ptr<IHanjaDic, UnknownReleaser> HJinterface;
|
|
|
|
bool OpenHanjaDic(LPCOLESTR lpszProgID) noexcept {
|
|
CLSID CLSID_HanjaDic;
|
|
HRESULT hr = CLSIDFromProgID(lpszProgID, &CLSID_HanjaDic);
|
|
if (SUCCEEDED(hr)) {
|
|
IHanjaDic *instance = nullptr;
|
|
hr = CoCreateInstance(CLSID_HanjaDic, nullptr,
|
|
CLSCTX_INPROC_SERVER, IID_IHanjaDic,
|
|
reinterpret_cast<LPVOID *>(&instance));
|
|
if (SUCCEEDED(hr) && instance) {
|
|
HJinterface.reset(instance);
|
|
hr = instance->OpenMainDic();
|
|
return SUCCEEDED(hr);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public:
|
|
bool Open() noexcept {
|
|
return OpenHanjaDic(OLESTR("imkrhjd.hanjadic"))
|
|
|| OpenHanjaDic(OLESTR("mshjdic.hanjadic"));
|
|
}
|
|
|
|
void Close() const noexcept {
|
|
HJinterface->CloseMainDic();
|
|
}
|
|
|
|
bool IsHanja(wchar_t hanja) const noexcept {
|
|
HANJA_TYPE hanjaType = HANJA_UNKNOWN;
|
|
const HRESULT hr = HJinterface->GetHanjaType(hanja, &hanjaType);
|
|
return SUCCEEDED(hr) && hanjaType > HANJA_UNKNOWN;
|
|
}
|
|
|
|
bool HanjaToHangul(const ScopedBSTR &bstrHanja, ScopedBSTR &bstrHangul) const noexcept {
|
|
BSTR result = nullptr;
|
|
const HRESULT hr = HJinterface->HanjaToHangul(bstrHanja.get(), &result);
|
|
bstrHangul.reset(result);
|
|
return SUCCEEDED(hr);
|
|
}
|
|
};
|
|
|
|
bool GetHangulOfHanja(std::wstring &inout) noexcept {
|
|
// Convert every hanja to hangul.
|
|
// Return whether any character been converted.
|
|
// Hanja linked to different notes in Hangul have different codes,
|
|
// so current character based conversion is enough.
|
|
// great thanks for BLUEnLIVE.
|
|
bool changed = false;
|
|
HanjaDic dict;
|
|
if (dict.Open()) {
|
|
for (wchar_t &character : inout) {
|
|
if (dict.IsHanja(character)) { // Pass hanja only!
|
|
ScopedBSTR bstrHangul;
|
|
if (dict.HanjaToHangul(ScopedBSTR(character), bstrHangul)) {
|
|
changed = true;
|
|
character = *(bstrHangul.get());
|
|
}
|
|
}
|
|
}
|
|
dict.Close();
|
|
}
|
|
return changed;
|
|
}
|
|
|
|
}
|