1/* 2 * Copyright (C) 2012 Igalia S.L. 3 * Copyright (C) 2012 Samsung Electronics 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Lesser General Public 7 * License as published by the Free Software Foundation; either 8 * version 2 of the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Lesser General Public License for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public 16 * License along with this library; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 */ 19 20#include "config.h" 21#include "TextCheckerEnchant.h" 22 23#if ENABLE(SPELLCHECK) 24 25#include <Language.h> 26#include <glib.h> 27#include <text/TextBreakIterator.h> 28 29namespace WebCore { 30 31static const size_t maximumNumberOfSuggestions = 10; 32 33static void enchantDictDescribeCallback(const char* const languageTag, const char* const, const char* const, const char* const, void* data) 34{ 35 Vector<CString>* dictionaries = static_cast<Vector<CString>*>(data); 36 dictionaries->append(languageTag); 37} 38 39TextCheckerEnchant::TextCheckerEnchant() 40 : m_broker(enchant_broker_init()) 41 , m_enchantDictionaries(0) 42{ 43} 44 45TextCheckerEnchant::~TextCheckerEnchant() 46{ 47 if (!m_broker) 48 return; 49 50 freeEnchantBrokerDictionaries(); 51 enchant_broker_free(m_broker); 52} 53 54void TextCheckerEnchant::ignoreWord(const String& word) 55{ 56 for (Vector<EnchantDict*>::const_iterator iter = m_enchantDictionaries.begin(); iter != m_enchantDictionaries.end(); ++iter) 57 enchant_dict_add_to_session(*iter, word.utf8().data(), -1); 58} 59 60void TextCheckerEnchant::learnWord(const String& word) 61{ 62 for (Vector<EnchantDict*>::const_iterator iter = m_enchantDictionaries.begin(); iter != m_enchantDictionaries.end(); ++iter) 63 enchant_dict_add(*iter, word.utf8().data(), -1); 64} 65 66void TextCheckerEnchant::checkSpellingOfWord(const CString& word, int start, int end, int& misspellingLocation, int& misspellingLength) 67{ 68 const char* string = word.data(); 69 char* startPtr = g_utf8_offset_to_pointer(string, start); 70 int numberOfBytes = static_cast<int>(g_utf8_offset_to_pointer(string, end) - startPtr); 71 72 for (Vector<EnchantDict*>::const_iterator dictIter = m_enchantDictionaries.begin(); dictIter != m_enchantDictionaries.end(); ++dictIter) { 73 if (!enchant_dict_check(*dictIter, startPtr, numberOfBytes)) { 74 // Stop checking, this word is ok in at least one dict. 75 misspellingLocation = -1; 76 misspellingLength = 0; 77 return; 78 } 79 } 80 81 misspellingLocation = start; 82 misspellingLength = end - start; 83} 84 85void TextCheckerEnchant::checkSpellingOfString(const String& string, int& misspellingLocation, int& misspellingLength) 86{ 87 // Assume that the words in the string are spelled correctly. 88 misspellingLocation = -1; 89 misspellingLength = 0; 90 91 if (!hasDictionary()) 92 return; 93 94 TextBreakIterator* iter = wordBreakIterator(string.characters(), string.length()); 95 if (!iter) 96 return; 97 98 CString utf8String = string.utf8(); 99 int start = textBreakFirst(iter); 100 for (int end = textBreakNext(iter); end != TextBreakDone; end = textBreakNext(iter)) { 101 if (isWordTextBreak(iter)) { 102 checkSpellingOfWord(utf8String, start, end, misspellingLocation, misspellingLength); 103 // Stop checking the next words If the current word is misspelled, to do not overwrite its misspelled location and length. 104 if (misspellingLength) 105 return; 106 } 107 start = end; 108 } 109} 110 111Vector<String> TextCheckerEnchant::getGuessesForWord(const String& word) 112{ 113 Vector<String> guesses; 114 if (!hasDictionary()) 115 return guesses; 116 117 for (Vector<EnchantDict*>::const_iterator iter = m_enchantDictionaries.begin(); iter != m_enchantDictionaries.end(); ++iter) { 118 size_t numberOfSuggestions; 119 size_t i; 120 121 char** suggestions = enchant_dict_suggest(*iter, word.utf8().data(), -1, &numberOfSuggestions); 122 if (numberOfSuggestions <= 0) 123 continue; 124 125 if (numberOfSuggestions > maximumNumberOfSuggestions) 126 numberOfSuggestions = maximumNumberOfSuggestions; 127 128 for (i = 0; i < numberOfSuggestions; i++) 129 guesses.append(String::fromUTF8(suggestions[i])); 130 131 enchant_dict_free_suggestions(*iter, suggestions); 132 } 133 134 return guesses; 135} 136 137void TextCheckerEnchant::updateSpellCheckingLanguages(const Vector<String>& languages) 138{ 139 Vector<EnchantDict*> spellDictionaries; 140 141 if (!languages.isEmpty()) { 142 for (Vector<String>::const_iterator iter = languages.begin(); iter != languages.end(); ++iter) { 143 CString currentLanguage = iter->utf8(); 144 if (enchant_broker_dict_exists(m_broker, currentLanguage.data())) { 145 EnchantDict* dict = enchant_broker_request_dict(m_broker, currentLanguage.data()); 146 spellDictionaries.append(dict); 147 } 148 } 149 } else { 150 // Languages are not specified by user, try to get default language. 151 CString utf8Language = defaultLanguage().utf8(); 152 const char* language = utf8Language.data(); 153 if (enchant_broker_dict_exists(m_broker, language)) { 154 EnchantDict* dict = enchant_broker_request_dict(m_broker, language); 155 spellDictionaries.append(dict); 156 } else { 157 // No dictionaries selected, we get the first one from the list. 158 Vector<CString> allDictionaries; 159 enchant_broker_list_dicts(m_broker, enchantDictDescribeCallback, &allDictionaries); 160 if (!allDictionaries.isEmpty()) { 161 EnchantDict* dict = enchant_broker_request_dict(m_broker, allDictionaries.first().data()); 162 spellDictionaries.append(dict); 163 } 164 } 165 } 166 freeEnchantBrokerDictionaries(); 167 m_enchantDictionaries = spellDictionaries; 168} 169 170Vector<String> TextCheckerEnchant::loadedSpellCheckingLanguages() const 171{ 172 Vector<String> languages; 173 if (!hasDictionary()) 174 return languages; 175 176 // Get a Vector<CString> with the list of languages in use. 177 Vector<CString> currentDictionaries; 178 for (Vector<EnchantDict*>::const_iterator iter = m_enchantDictionaries.begin(); iter != m_enchantDictionaries.end(); ++iter) 179 enchant_dict_describe(*iter, enchantDictDescribeCallback, ¤tDictionaries); 180 181 for (Vector<CString>::const_iterator iter = currentDictionaries.begin(); iter != currentDictionaries.end(); ++iter) 182 languages.append(String::fromUTF8(iter->data())); 183 184 return languages; 185} 186 187Vector<String> TextCheckerEnchant::availableSpellCheckingLanguages() const 188{ 189 Vector<CString> allDictionaries; 190 enchant_broker_list_dicts(m_broker, enchantDictDescribeCallback, &allDictionaries); 191 192 Vector<String> languages; 193 for (Vector<CString>::const_iterator iter = allDictionaries.begin(); iter != allDictionaries.end(); ++iter) 194 languages.append(String::fromUTF8(iter->data())); 195 196 return languages; 197} 198 199void TextCheckerEnchant::freeEnchantBrokerDictionaries() 200{ 201 for (Vector<EnchantDict*>::const_iterator iter = m_enchantDictionaries.begin(); iter != m_enchantDictionaries.end(); ++iter) 202 enchant_broker_free_dict(m_broker, *iter); 203} 204 205} // namespace WebCore 206 207#endif // ENABLE(SPELLCHECK) 208 209