initial import
[vuplus_webkit] / Source / WebKit / gtk / webkit / webkitspellcheckerenchant.cpp
1 /*
2  *  Copyright (C) 2011 Igalia S.L.
3  *
4  *  This library is free software; you can redistribute it and/or
5  *  modify it under the terms of the GNU Lesser General Public
6  *  License as published by the Free Software Foundation; either
7  *  version 2 of the License, or (at your option) any later version.
8  *
9  *  This library is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  *  Lesser General Public License for more details.
13  *
14  *  You should have received a copy of the GNU Lesser General Public
15  *  License along with this library; if not, write to the Free Software
16  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17  */
18
19 #include "config.h"
20 #include "webkitspellcheckerenchant.h"
21
22 #if ENABLE(SPELLCHECK)
23
24 #include "GOwnPtr.h"
25 #include "webkitspellchecker.h"
26 #include <enchant.h>
27 #include <gtk/gtk.h>
28 #include <wtf/text/CString.h>
29
30 static EnchantBroker* broker = 0;
31
32 struct _WebKitSpellCheckerEnchantPrivate {
33     GSList* enchantDicts;
34 };
35
36 static void webkit_spell_checker_enchant_spell_checker_interface_init(WebKitSpellCheckerInterface* interface);
37
38 G_DEFINE_TYPE_WITH_CODE(WebKitSpellCheckerEnchant, webkit_spell_checker_enchant, G_TYPE_OBJECT,
39                         G_IMPLEMENT_INTERFACE(WEBKIT_TYPE_SPELL_CHECKER,
40                                               webkit_spell_checker_enchant_spell_checker_interface_init))
41
42 static void createEnchantBrokerIfNeeded()
43 {
44     if (!broker)
45         broker = enchant_broker_init();
46 }
47
48 static void freeSpellCheckingLanguage(gpointer data, gpointer)
49 {
50     createEnchantBrokerIfNeeded();
51
52     enchant_broker_free_dict(broker, static_cast<EnchantDict*>(data));
53 }
54
55 static void webkit_spell_checker_enchant_finalize(GObject* object)
56 {
57     WebKitSpellCheckerEnchantPrivate* priv = WEBKIT_SPELL_CHECKER_ENCHANT(object)->priv;
58
59     g_slist_foreach(priv->enchantDicts, freeSpellCheckingLanguage, 0);
60     g_slist_free(priv->enchantDicts);
61
62     WEBKIT_SPELL_CHECKER_ENCHANT(object)->priv->~WebKitSpellCheckerEnchantPrivate();
63 }
64
65 static void webkit_spell_checker_enchant_class_init(WebKitSpellCheckerEnchantClass* klass)
66 {
67     GObjectClass* objectClass = G_OBJECT_CLASS(klass);
68
69     objectClass->finalize = webkit_spell_checker_enchant_finalize;
70
71     g_type_class_add_private(klass, sizeof(WebKitSpellCheckerEnchantPrivate));
72 }
73
74 static void webkit_spell_checker_enchant_init(WebKitSpellCheckerEnchant* checker)
75 {
76     WebKitSpellCheckerEnchantPrivate* priv = G_TYPE_INSTANCE_GET_PRIVATE(checker, WEBKIT_TYPE_SPELL_CHECKER_ENCHANT, WebKitSpellCheckerEnchantPrivate);
77     checker->priv = priv;
78     new (priv) WebKitSpellCheckerEnchantPrivate();
79
80     priv->enchantDicts = 0;
81 }
82
83 static void checkSpellingOfString(WebKitSpellChecker* checker, const char* string, int* misspellingLocation, int* misspellingLength)
84 {
85     WebKitSpellCheckerEnchantPrivate* priv = WEBKIT_SPELL_CHECKER_ENCHANT(checker)->priv;
86
87     GSList* dicts = priv->enchantDicts;
88     if (!dicts)
89         return;
90
91     int length = g_utf8_strlen(string, -1);
92
93     PangoLanguage* language(pango_language_get_default());
94     GOwnPtr<PangoLogAttr> attrs(g_new(PangoLogAttr, length + 1));
95
96     // pango_get_log_attrs uses an aditional position at the end of the text.
97     pango_get_log_attrs(string, -1, -1, language, attrs.get(), length + 1);
98
99     for (int i = 0; i < length + 1; i++) {
100         // We go through each character until we find an is_word_start,
101         // then we get into an inner loop to find the is_word_end corresponding
102         // to it.
103         if (attrs.get()[i].is_word_start) {
104             int start = i;
105             int end = i;
106             int wordLength;
107
108             while (attrs.get()[end].is_word_end < 1)
109                 end++;
110
111             wordLength = end - start;
112             // Set the iterator to be at the current word end, so we don't
113             // check characters twice.
114             i = end;
115
116             gchar* cstart = g_utf8_offset_to_pointer(string, start);
117             gint bytes = static_cast<gint>(g_utf8_offset_to_pointer(string, end) - cstart);
118             GOwnPtr<gchar> word(g_new0(gchar, bytes + 1));
119
120             g_utf8_strncpy(word.get(), cstart, wordLength);
121
122             for (; dicts; dicts = dicts->next) {
123                 EnchantDict* dict = static_cast<EnchantDict*>(dicts->data);
124                 if (enchant_dict_check(dict, word.get(), wordLength)) {
125                     *misspellingLocation = start;
126                     *misspellingLength = wordLength;
127                 } else {
128                     // Stop checking, this word is ok in at least one dict.
129                     *misspellingLocation = -1;
130                     *misspellingLength = 0;
131                     break;
132                 }
133             }
134         }
135     }
136 }
137
138 static char** getGuessesForWord(WebKitSpellChecker* checker, const char* word, const char* context)
139 {
140     WebKitSpellCheckerEnchantPrivate* priv = WEBKIT_SPELL_CHECKER_ENCHANT(checker)->priv;
141
142     GSList* dicts = priv->enchantDicts;
143     char** guesses = 0;
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, -1, &numberOfSuggestions);
151
152         if (numberOfSuggestions > 0) {
153             if (numberOfSuggestions > 10)
154                 numberOfSuggestions = 10;
155
156             guesses = static_cast<char**>(g_malloc0((numberOfSuggestions + 1) * sizeof(char*)));
157             for (i = 0; i < numberOfSuggestions && i < 10; i++)
158                 guesses[i] = g_strdup(suggestions[i]);
159
160             guesses[i] = 0;
161
162             enchant_dict_free_suggestions(dict, suggestions);
163         }
164     }
165
166     return guesses;
167 }
168
169 static void getAvailableDictionariesCallback(const char* const languageTag, const char* const, const char* const, const char* const, void* data)
170 {
171     Vector<CString>* dicts = static_cast<Vector<CString>*>(data);
172
173     dicts->append(languageTag);
174 }
175
176 static void updateSpellCheckingLanguages(WebKitSpellChecker* checker, const char* languages)
177 {
178     GSList* spellDictionaries = 0;
179
180     WebKitSpellCheckerEnchantPrivate* priv = WEBKIT_SPELL_CHECKER_ENCHANT(checker)->priv;
181
182     createEnchantBrokerIfNeeded();
183
184     if (languages) {
185         char** langs = g_strsplit(languages, ",", -1);
186         for (int i = 0; langs[i]; i++) {
187             if (enchant_broker_dict_exists(broker, langs[i])) {
188                 EnchantDict* dict = enchant_broker_request_dict(broker, langs[i]);
189                 spellDictionaries = g_slist_append(spellDictionaries, dict);
190             }
191         }
192         g_strfreev(langs);
193     } else {
194         const char* language = pango_language_to_string(gtk_get_default_language());
195         if (enchant_broker_dict_exists(broker, language)) {
196             EnchantDict* dict = enchant_broker_request_dict(broker, language);
197             spellDictionaries = g_slist_append(spellDictionaries, dict);
198         } else {
199             // No dictionaries selected, we get one from the list.
200             Vector<CString> allDictionaries;
201             enchant_broker_list_dicts(broker, getAvailableDictionariesCallback, &allDictionaries);
202             if (!allDictionaries.isEmpty()) {
203                 EnchantDict* dict = enchant_broker_request_dict(broker, allDictionaries[0].data());
204                 spellDictionaries = g_slist_append(spellDictionaries, dict);
205             }
206         }
207     }
208     g_slist_foreach(priv->enchantDicts, freeSpellCheckingLanguage, 0);
209     g_slist_free(priv->enchantDicts);
210     priv->enchantDicts = spellDictionaries;
211 }
212
213 static char* getAutocorrectSuggestionsForMisspelledWord(WebKitSpellChecker* checker, const char* word)
214 {
215     return 0;
216 }
217
218 static void learnWord(WebKitSpellChecker* checker, const char* word)
219 {
220     WebKitSpellCheckerEnchantPrivate* priv = WEBKIT_SPELL_CHECKER_ENCHANT(checker)->priv;
221     GSList* dicts = priv->enchantDicts;
222
223     for (; dicts; dicts = dicts->next) {
224         EnchantDict* dict = static_cast<EnchantDict*>(dicts->data);
225
226         enchant_dict_add_to_personal(dict, word, -1);
227     }
228 }
229
230 static void ignoreWord(WebKitSpellChecker* checker, const char* word)
231 {
232     WebKitSpellCheckerEnchantPrivate* priv = WEBKIT_SPELL_CHECKER_ENCHANT(checker)->priv;
233     GSList* dicts = priv->enchantDicts;
234
235     for (; dicts; dicts = dicts->next) {
236         EnchantDict* dict = static_cast<EnchantDict*>(dicts->data);
237
238         enchant_dict_add_to_session(dict, word, -1);
239     }
240 }
241
242 static void webkit_spell_checker_enchant_spell_checker_interface_init(WebKitSpellCheckerInterface* interface)
243 {
244     interface->check_spelling_of_string = checkSpellingOfString;
245     interface->get_guesses_for_word = getGuessesForWord;
246     interface->update_spell_checking_languages = updateSpellCheckingLanguages;
247     interface->get_autocorrect_suggestions_for_misspelled_word = getAutocorrectSuggestionsForMisspelledWord;
248     interface->learn_word = learnWord;
249     interface->ignore_word = ignoreWord;
250 }
251
252 #endif /* ENABLE(SPELLCHECK) */
253