1 /* 2 * Copyright (C) 2007 Alp Toker <alp (at) atoker.com> 3 * Copyright (C) 2008 Nuanti Ltd. 4 * Copyright (C) 2009 Diego Escalante Urrelo <diegoe (at) gnome.org> 5 * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. 6 * Copyright (C) 2009, 2010 Igalia S.L. 7 * Copyright (C) 2010, Martin Robinson <mrobinson (at) webkit.org> 8 * 9 * This library is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU Lesser General Public 11 * License as published by the Free Software Foundation; either 12 * version 2 of the License, or (at your option) any later version. 13 * 14 * This library is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * Lesser General Public License for more details. 18 * 19 * You should have received a copy of the GNU Lesser General Public 20 * License along with this library; if not, write to the Free Software 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 22 */ 23 24 #include "config.h" 25 #include "TextCheckerClientEnchant.h" 26 27 #include "NotImplemented.h" 28 #include "webkitwebsettingsprivate.h" 29 #include "webkitwebviewprivate.h" 30 #include <enchant.h> 31 #include <glib.h> 32 #include <wtf/text/CString.h> 33 34 using namespace WebCore; 35 36 namespace WebKit { 37 38 EnchantBroker* TextCheckerClientEnchant::broker = 0; 39 40 TextCheckerClientEnchant::TextCheckerClientEnchant(WebKitWebView* webView) 41 : m_webView(webView) 42 , m_enchantDicts(0) 43 { 44 } 45 46 TextCheckerClientEnchant::~TextCheckerClientEnchant() 47 { 48 g_slist_foreach(m_enchantDicts, freeSpellCheckingLanguage, 0); 49 g_slist_free(m_enchantDicts); 50 } 51 52 void TextCheckerClientEnchant::ignoreWordInSpellDocument(const String& text) 53 { 54 GSList* dicts = m_enchantDicts; 55 56 for (; dicts; dicts = dicts->next) { 57 EnchantDict* dict = static_cast<EnchantDict*>(dicts->data); 58 59 enchant_dict_add_to_session(dict, text.utf8().data(), -1); 60 } 61 } 62 63 void TextCheckerClientEnchant::learnWord(const String& text) 64 { 65 GSList* dicts = m_enchantDicts; 66 67 for (; dicts; dicts = dicts->next) { 68 EnchantDict* dict = static_cast<EnchantDict*>(dicts->data); 69 70 enchant_dict_add_to_personal(dict, text.utf8().data(), -1); 71 } 72 } 73 74 void TextCheckerClientEnchant::checkSpellingOfString(const UChar* text, int length, int* misspellingLocation, int* misspellingLength) 75 { 76 GSList* dicts = m_enchantDicts; 77 if (!dicts) 78 return; 79 80 GOwnPtr<gchar> utf8Text(g_utf16_to_utf8(const_cast<gunichar2*>(text), length, 0, 0, 0)); 81 int utf8Length = g_utf8_strlen(utf8Text.get(), -1); 82 83 PangoLanguage* language(pango_language_get_default()); 84 GOwnPtr<PangoLogAttr> attrs(g_new(PangoLogAttr, utf8Length + 1)); 85 86 // pango_get_log_attrs uses an aditional position at the end of the text. 87 pango_get_log_attrs(utf8Text.get(), -1, -1, language, attrs.get(), utf8Length + 1); 88 89 for (int i = 0; i < length + 1; i++) { 90 // We go through each character until we find an is_word_start, 91 // then we get into an inner loop to find the is_word_end corresponding 92 // to it. 93 if (attrs.get()[i].is_word_start) { 94 int start = i; 95 int end = i; 96 int wordLength; 97 98 while (attrs.get()[end].is_word_end < 1) 99 end++; 100 101 wordLength = end - start; 102 // Set the iterator to be at the current word end, so we don't 103 // check characters twice. 104 i = end; 105 106 gchar* cstart = g_utf8_offset_to_pointer(utf8Text.get(), start); 107 gint bytes = static_cast<gint>(g_utf8_offset_to_pointer(utf8Text.get(), end) - cstart); 108 GOwnPtr<gchar> word(g_new0(gchar, bytes + 1)); 109 110 g_utf8_strncpy(word.get(), cstart, wordLength); 111 112 for (; dicts; dicts = dicts->next) { 113 EnchantDict* dict = static_cast<EnchantDict*>(dicts->data); 114 if (enchant_dict_check(dict, word.get(), wordLength)) { 115 *misspellingLocation = start; 116 *misspellingLength = wordLength; 117 } else { 118 // Stop checking, this word is ok in at least one dict. 119 *misspellingLocation = -1; 120 *misspellingLength = 0; 121 break; 122 } 123 } 124 } 125 } 126 } 127 128 String TextCheckerClientEnchant::getAutoCorrectSuggestionForMisspelledWord(const String& inputWord) 129 { 130 // This method can be implemented using customized algorithms for the particular browser. 131 // Currently, it computes an empty string. 132 return String(); 133 } 134 135 void TextCheckerClientEnchant::checkGrammarOfString(const UChar*, int, Vector<GrammarDetail>&, int*, int*) 136 { 137 notImplemented(); 138 } 139 140 void TextCheckerClientEnchant::getGuessesForWord(const String& word, const String& context, WTF::Vector<String>& guesses) 141 { 142 GSList* dicts = m_enchantDicts; 143 guesses.clear(); 144 145 for (; dicts; dicts = dicts->next) { 146 size_t numberOfSuggestions; 147 size_t i; 148 149 EnchantDict* dict = static_cast<EnchantDict*>(dicts->data); 150 gchar** suggestions = enchant_dict_suggest(dict, word.utf8().data(), -1, &numberOfSuggestions); 151 152 for (i = 0; i < numberOfSuggestions && i < 10; i++) 153 guesses.append(String::fromUTF8(suggestions[i])); 154 155 if (numberOfSuggestions > 0) 156 enchant_dict_free_suggestions(dict, suggestions); 157 } 158 } 159 160 static void getAvailableDictionariesCallback(const char* const languageTag, const char* const, const char* const, const char* const, void* data) 161 { 162 Vector<CString>* dicts = static_cast<Vector<CString>*>(data); 163 164 dicts->append(languageTag); 165 } 166 167 void TextCheckerClientEnchant::updateSpellCheckingLanguage(const char* spellCheckingLanguages) 168 { 169 EnchantDict* dict; 170 GSList* spellDictionaries = 0; 171 172 if (!broker) 173 broker = enchant_broker_init(); 174 175 if (spellCheckingLanguages) { 176 char** langs = g_strsplit(spellCheckingLanguages, ",", -1); 177 for (int i = 0; langs[i]; i++) { 178 if (enchant_broker_dict_exists(broker, langs[i])) { 179 dict = enchant_broker_request_dict(broker, langs[i]); 180 spellDictionaries = g_slist_append(spellDictionaries, dict); 181 } 182 } 183 g_strfreev(langs); 184 } else { 185 const char* language = pango_language_to_string(gtk_get_default_language()); 186 if (enchant_broker_dict_exists(broker, language)) { 187 dict = enchant_broker_request_dict(broker, language); 188 spellDictionaries = g_slist_append(spellDictionaries, dict); 189 } else { 190 // No dictionaries selected, we get one from the list 191 Vector<CString> allDictionaries; 192 enchant_broker_list_dicts(broker, getAvailableDictionariesCallback, &allDictionaries); 193 if (!allDictionaries.isEmpty()) { 194 dict = enchant_broker_request_dict(broker, allDictionaries[0].data()); 195 spellDictionaries = g_slist_append(spellDictionaries, dict); 196 } 197 } 198 } 199 g_slist_foreach(m_enchantDicts, freeSpellCheckingLanguage, 0); 200 g_slist_free(m_enchantDicts); 201 m_enchantDicts = spellDictionaries; 202 } 203 204 void TextCheckerClientEnchant::freeSpellCheckingLanguage(gpointer data, gpointer) 205 { 206 if (!broker) 207 broker = enchant_broker_init(); 208 209 EnchantDict* dict = static_cast<EnchantDict*>(data); 210 enchant_broker_free_dict(broker, dict); 211 } 212 213 } 214