diff options
author | TingPing <tingping@tingping.se> | 2015-02-20 21:58:31 -0500 |
---|---|---|
committer | TingPing <tingping@tingping.se> | 2015-02-21 05:54:58 -0500 |
commit | 96026b82c784bbba010d0a9550301cefd6c73af7 (patch) | |
tree | ff4ec77067f79426798bd0665fc04dbd3e1c6dc3 /src | |
parent | e42da22716d3d95fcf71ed5f36b6f5e4a9c3627d (diff) |
Add support for native win8+ spell checking
Diffstat (limited to 'src')
-rw-r--r-- | src/libenchant_win8/libenchant_win8.def | 2 | ||||
-rw-r--r-- | src/libenchant_win8/libenchant_win8.vcxproj | 47 | ||||
-rw-r--r-- | src/libenchant_win8/libenchant_win8.vcxproj.filters | 22 | ||||
-rw-r--r-- | src/libenchant_win8/win8_provider.cpp | 293 |
4 files changed, 364 insertions, 0 deletions
diff --git a/src/libenchant_win8/libenchant_win8.def b/src/libenchant_win8/libenchant_win8.def new file mode 100644 index 00000000..cf367651 --- /dev/null +++ b/src/libenchant_win8/libenchant_win8.def @@ -0,0 +1,2 @@ +EXPORTS +init_enchant_provider diff --git a/src/libenchant_win8/libenchant_win8.vcxproj b/src/libenchant_win8/libenchant_win8.vcxproj new file mode 100644 index 00000000..aab7acc8 --- /dev/null +++ b/src/libenchant_win8/libenchant_win8.vcxproj @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{BF0EBC16-68AD-4CD1-864C-5B56836EBE2A}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>libenchant_win8</RootNamespace> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v120</PlatformToolset> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <Import Project="..\..\win32\hexchat.props" /> + <ImportGroup Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup> + <OutDir>$(HexChatRel)lib\enchant\</OutDir> + <TargetName>libenchant_win8</TargetName> + </PropertyGroup> + <ItemDefinitionGroup> + <ClCompile> + <AdditionalIncludeDirectories>..\common;$(HexChatLib);$(DepsRoot)\include\enchant;$(DepsRoot)\include;$(Glib);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + </ClCompile> + <Link> + <AdditionalDependencies>$(DepLibs);%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalLibraryDirectories>$(DepsRoot)\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <ModuleDefinitionFile>libenchant_win8.def</ModuleDefinitionFile> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="win8_provider.cpp" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> +</Project> diff --git a/src/libenchant_win8/libenchant_win8.vcxproj.filters b/src/libenchant_win8/libenchant_win8.vcxproj.filters new file mode 100644 index 00000000..ff2f3024 --- /dev/null +++ b/src/libenchant_win8/libenchant_win8.vcxproj.filters @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="win8_provider.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> +</Project> diff --git a/src/libenchant_win8/win8_provider.cpp b/src/libenchant_win8/win8_provider.cpp new file mode 100644 index 00000000..73f16610 --- /dev/null +++ b/src/libenchant_win8/win8_provider.cpp @@ -0,0 +1,293 @@ +/* HexChat + * Copyright (c) 2015 Patrick Griffis + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "config.h" + +#include <Spellcheck.h> +#include <glib.h> + +#include "typedef.h" // for ssize_t +#include <enchant-provider.h> + +ENCHANT_PLUGIN_DECLARE ("win8") + +/* --------- Utils ----------*/ + +static char * +utf16_to_utf8 (const wchar_t * const str, gboolean from_bcp47) +{ + char *utf8 = g_utf16_to_utf8 ((gunichar2*)str, -1, nullptr, nullptr, nullptr); + if (utf8 && from_bcp47) + { + char *p = utf8; + /* bcp47 tags use syntax "en-US" while the myspell versions are "en_US" */ + while (*p) + { + if (*p == '-') + *p = '_'; + p++; + } + } + return utf8; +} + +static wchar_t * +utf8_to_utf16 (const char * const str, size_t len, gboolean to_bcp47) +{ + wchar_t *utf16 = (wchar_t*)g_utf8_to_utf16 (str, len, nullptr, nullptr, nullptr); + if (utf16 && to_bcp47) + { + wchar_t *p = utf16; + /* bcp47 tags use syntax "en-US" while the myspell versions are "en_US" */ + while (*p) + { + if (*p == L'_') + *p = L'-'; + p++; + } + } + return utf16; +} + +static char ** +enumstring_to_chararray (IEnumString *strings, size_t *out_len, gboolean from_bcp47) +{ + char **chars = g_new (char*, 256); /* Hopefully large enough */ + LPOLESTR wstr = nullptr; + size_t i = 0; + + while (SUCCEEDED (strings->Next (1, &wstr, nullptr)) && i < 256 && wstr) + { + char *str = utf16_to_utf8 (wstr, from_bcp47); + if (str) + { + chars[i] = str; + i++; + } + CoTaskMemFree (wstr); + } + chars[i] = nullptr; + strings->Release (); + + *out_len = i; + return chars; +} + +/* ---------- Dict ------------ */ + +static void +win8_dict_add_to_personal (EnchantDict *dict, const char *const word, size_t len) +{ + auto checker = static_cast<ISpellChecker*>(dict->user_data); + wchar_t *wword = utf8_to_utf16 (word, len, FALSE); + + checker->Add (wword); + g_free (wword); +} + +static void +win8_dict_add_to_session (EnchantDict *dict, const char *const word, size_t len) +{ + auto checker = static_cast<ISpellChecker*>(dict->user_data); + wchar_t *wword = utf8_to_utf16 (word, len, FALSE); + + checker->Ignore (wword); + g_free (wword); +} + +static int +win8_dict_check (EnchantDict *dict, const char *const word, size_t len) +{ + auto checker = static_cast<ISpellChecker*>(dict->user_data); + wchar_t *wword = utf8_to_utf16 (word, len, FALSE); + IEnumSpellingError *errors; + ISpellingError *error = nullptr; + HRESULT hr; + + hr = checker->Check (wword, &errors); + g_free (wword); + + if (FAILED (hr)) + return -1; /* Error */ + + if (errors->Next (&error) == S_OK) + { + error->Release (); + errors->Release (); + return 1; /* Spelling Issue */ + } + else + { + errors->Release (); + return 0; /* Correct */ + } +} + +static char ** +win8_dict_suggest (EnchantDict *dict, const char *const word, size_t len, size_t *out_n_suggs) +{ + auto checker = static_cast<ISpellChecker*>(dict->user_data); + wchar_t *wword = utf8_to_utf16 (word, len, FALSE); + IEnumString *suggestions; + HRESULT hr; + + hr = checker->Suggest (wword, &suggestions); + g_free (wword); + + if (FAILED (hr)) + { + *out_n_suggs = 0; + return nullptr; + } + + return enumstring_to_chararray (suggestions, out_n_suggs, FALSE); +} + +/* ---------- Provider ------------ */ + +static EnchantDict * +win8_provider_request_dict (EnchantProvider *provider, const char *const tag) +{ + auto factory = static_cast<ISpellCheckerFactory*>(provider->user_data); + ISpellChecker *checker; + EnchantDict *dict; + wchar_t *wtag = utf8_to_utf16 (tag, -1, TRUE); + HRESULT hr; + + hr = factory->CreateSpellChecker (wtag, &checker); + g_free (wtag); + + if (FAILED (hr)) + return nullptr; + + dict = g_new0 (EnchantDict, 1); + dict->suggest = win8_dict_suggest; + dict->check = win8_dict_check; + dict->add_to_personal = win8_dict_add_to_personal; + dict->add_to_exclude = win8_dict_add_to_personal; /* Basically the same */ + dict->add_to_session = win8_dict_add_to_session; + + dict->user_data = checker; + + return dict; +} + +static void +win8_provider_dispose_dict (EnchantProvider *provider, EnchantDict *dict) +{ + if (dict) + { + auto checker = static_cast<ISpellChecker*>(dict->user_data); + + checker->Release (); + g_free (dict); + } +} + +static int +win8_provider_dictionary_exists (EnchantProvider *provider, const char *const tag) +{ + auto factory = static_cast<ISpellCheckerFactory*>(provider->user_data); + wchar_t *wtag = utf8_to_utf16 (tag, -1, TRUE); + + BOOL is_supported = FALSE; + factory->IsSupported (wtag, &is_supported); + + g_free (wtag); + return is_supported; +} + + +static char ** +win8_provider_list_dicts (EnchantProvider *provider, size_t *out_n_dicts) +{ + auto factory = static_cast<ISpellCheckerFactory*>(provider->user_data); + IEnumString *dicts; + + if (FAILED(factory->get_SupportedLanguages (&dicts))) + { + *out_n_dicts = 0; + return nullptr; + } + + return enumstring_to_chararray (dicts, out_n_dicts, TRUE); +} + +static void +win8_provider_free_string_list (EnchantProvider *provider, char **str_list) +{ + g_strfreev (str_list); +} + +static void +win8_provider_dispose (EnchantProvider *provider) +{ + if (provider) + { + auto factory = static_cast<ISpellCheckerFactory*>(provider->user_data); + + factory->Release(); + g_free (provider); + } +} + +static const char * +win8_provider_identify (EnchantProvider *provider) +{ + return "win8"; +} + +static const char * +win8_provider_describe (EnchantProvider *provider) +{ + return "Windows 8 SpellCheck Provider"; +} + +extern "C" +{ + +EnchantProvider * +init_enchant_provider (void) +{ + EnchantProvider *provider; + ISpellCheckerFactory *factory; + + if (FAILED (CoCreateInstance (__uuidof(SpellCheckerFactory), nullptr, + CLSCTX_INPROC_SERVER, IID_PPV_ARGS (&factory)))) + return nullptr; + + provider = g_new0 (EnchantProvider, 1); + provider->dispose = win8_provider_dispose; + provider->request_dict = win8_provider_request_dict; + provider->dispose_dict = win8_provider_dispose_dict; + provider->dictionary_exists = win8_provider_dictionary_exists; + provider->identify = win8_provider_identify; + provider->describe = win8_provider_describe; + provider->list_dicts = win8_provider_list_dicts; + provider->free_string_list = win8_provider_free_string_list; + + provider->user_data = factory; + + return provider; +} + +} |