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 @@
+
+
+
+
+ Release
+ Win32
+
+
+ Release
+ x64
+
+
+
+ {BF0EBC16-68AD-4CD1-864C-5B56836EBE2A}
+ Win32Proj
+ libenchant_win8
+
+
+
+ DynamicLibrary
+ true
+ v120
+
+
+
+
+
+
+
+ $(HexChatRel)lib\enchant\
+ libenchant_win8
+
+
+
+ ..\common;$(HexChatLib);$(DepsRoot)\include\enchant;$(DepsRoot)\include;$(Glib);%(AdditionalIncludeDirectories)
+
+
+ $(DepLibs);%(AdditionalDependencies)
+ $(DepsRoot)\lib;%(AdditionalLibraryDirectories)
+ libenchant_win8.def
+
+
+
+
+
+
+
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 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;hm;inl;inc;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
+
+ Source Files
+
+
+
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
+#include
+
+#include "typedef.h" // for ssize_t
+#include
+
+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(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(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(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(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(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(dict->user_data);
+
+ checker->Release ();
+ g_free (dict);
+ }
+}
+
+static int
+win8_provider_dictionary_exists (EnchantProvider *provider, const char *const tag)
+{
+ auto factory = static_cast(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(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(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;
+}
+
+}
diff --git a/win32/hexchat.sln b/win32/hexchat.sln
index 3cc4d918..266231f0 100644
--- a/win32/hexchat.sln
+++ b/win32/hexchat.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
-VisualStudioVersion = 12.0.21005.1
+VisualStudioVersion = 12.0.30501.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "common", "..\src\common\common.vcxproj", "{87554B59-006C-4D94-9714-897B27067BA3}"
ProjectSection(ProjectDependencies) = postProject
@@ -123,6 +123,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "python3", "..\plugins\pytho
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "notifications-winrt", "..\src\fe-gtk\notifications\notifications-winrt.vcxproj", "{C53145CC-D021-40C9-B97C-0249AB9A43C9}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "external", "external", "{021EC1D0-FF67-4700-9AB2-EAABF1159C09}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libenchant_win8", "..\src\libenchant_win8\libenchant_win8.vcxproj", "{BF0EBC16-68AD-4CD1-864C-5B56836EBE2A}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Release|Win32 = Release|Win32
@@ -209,6 +213,10 @@ Global
{C53145CC-D021-40C9-B97C-0249AB9A43C9}.Release|Win32.Build.0 = Release|Win32
{C53145CC-D021-40C9-B97C-0249AB9A43C9}.Release|x64.ActiveCfg = Release|x64
{C53145CC-D021-40C9-B97C-0249AB9A43C9}.Release|x64.Build.0 = Release|x64
+ {BF0EBC16-68AD-4CD1-864C-5B56836EBE2A}.Release|Win32.ActiveCfg = Release|Win32
+ {BF0EBC16-68AD-4CD1-864C-5B56836EBE2A}.Release|Win32.Build.0 = Release|Win32
+ {BF0EBC16-68AD-4CD1-864C-5B56836EBE2A}.Release|x64.ActiveCfg = Release|x64
+ {BF0EBC16-68AD-4CD1-864C-5B56836EBE2A}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -234,5 +242,6 @@ Global
{D90BC3E3-1341-4849-9354-5F40489D39D1} = {D237DA6B-BD5F-46C0-8BEA-50E9A1340240}
{C2321A03-0BA7-45B3-8740-ABD82B36B0BF} = {D237DA6B-BD5F-46C0-8BEA-50E9A1340240}
{C53145CC-D021-40C9-B97C-0249AB9A43C9} = {561126F4-FA18-45FC-A2BF-8F858F161D6D}
+ {BF0EBC16-68AD-4CD1-864C-5B56836EBE2A} = {021EC1D0-FF67-4700-9AB2-EAABF1159C09}
EndGlobalSection
EndGlobal
diff --git a/win32/installer/hexchat.iss.tt b/win32/installer/hexchat.iss.tt
index 5ee9a0ae..9f55e8fd 100644
--- a/win32/installer/hexchat.iss.tt
+++ b/win32/installer/hexchat.iss.tt
@@ -150,7 +150,7 @@ Source: "zlib1.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: libs
Source: "plugins\hcnotifications-winrt.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: libs
-Source: "lib\enchant\libenchant_myspell.dll"; DestDir: "{app}\lib\enchant"; Flags: ignoreversion; Components: libs
+Source: "lib\enchant\*"; DestDir: "{app}\lib\enchant"; Flags: ignoreversion; Components: libs
Source: "lib\gtk-2.0\i686-pc-vs10\engines\*"; DestDir: "{app}\lib\gtk-2.0\i686-pc-vs10\engines"; Flags: ignoreversion; Components: libs