1 /*
2 * Copyright (C) 2007 Alp Toker <alp@atoker.com>
3 * Copyright (C) 2008 Nuanti Ltd.
4 * Copyright (C) 2009 Diego Escalante Urrelo <diegoe@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@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
TextCheckerClientEnchant(WebKitWebView * webView)40 TextCheckerClientEnchant::TextCheckerClientEnchant(WebKitWebView* webView)
41 : m_webView(webView)
42 , m_enchantDicts(0)
43 {
44 }
45
~TextCheckerClientEnchant()46 TextCheckerClientEnchant::~TextCheckerClientEnchant()
47 {
48 g_slist_foreach(m_enchantDicts, freeSpellCheckingLanguage, 0);
49 g_slist_free(m_enchantDicts);
50 }
51
ignoreWordInSpellDocument(const String & text)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
learnWord(const String & text)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
checkSpellingOfString(const UChar * text,int length,int * misspellingLocation,int * misspellingLength)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
getAutoCorrectSuggestionForMisspelledWord(const String & inputWord)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
checkGrammarOfString(const UChar *,int,Vector<GrammarDetail> &,int *,int *)135 void TextCheckerClientEnchant::checkGrammarOfString(const UChar*, int, Vector<GrammarDetail>&, int*, int*)
136 {
137 notImplemented();
138 }
139
getGuessesForWord(const String & word,const String & context,WTF::Vector<String> & guesses)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
getAvailableDictionariesCallback(const char * const languageTag,const char * const,const char * const,const char * const,void * data)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
updateSpellCheckingLanguage(const char * spellCheckingLanguages)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
freeSpellCheckingLanguage(gpointer data,gpointer)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