tizen beta release
[profile/ivi/webkit-efl.git] / 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 /**
31  * SECTION:webkitspellcheckerenchant
32  * @short_description: the default spell checking implementation for WebKitGTK+.
33  *
34  * #WebKitSpellCheckerEnchant is the default spell checking implementation for
35  * WebKitGTK+. It uses the Enchant dictionaries installed on the system to
36  * correct spelling.
37  */
38 static EnchantBroker* broker = 0;
39
40 struct _WebKitSpellCheckerEnchantPrivate {
41     GSList* enchantDicts;
42 };
43
44 static void webkit_spell_checker_enchant_spell_checker_interface_init(WebKitSpellCheckerInterface* interface);
45
46 G_DEFINE_TYPE_WITH_CODE(WebKitSpellCheckerEnchant, webkit_spell_checker_enchant, G_TYPE_OBJECT,
47                         G_IMPLEMENT_INTERFACE(WEBKIT_TYPE_SPELL_CHECKER,
48                                               webkit_spell_checker_enchant_spell_checker_interface_init))
49
50 static void createEnchantBrokerIfNeeded()
51 {
52     if (!broker)
53         broker = enchant_broker_init();
54 }
55
56 static void freeSpellCheckingLanguage(gpointer data, gpointer)
57 {
58     createEnchantBrokerIfNeeded();
59
60     enchant_broker_free_dict(broker, static_cast<EnchantDict*>(data));
61 }
62
63 static void webkit_spell_checker_enchant_finalize(GObject* object)
64 {
65     WebKitSpellCheckerEnchantPrivate* priv = WEBKIT_SPELL_CHECKER_ENCHANT(object)->priv;
66
67     g_slist_foreach(priv->enchantDicts, freeSpellCheckingLanguage, 0);
68     g_slist_free(priv->enchantDicts);
69
70     WEBKIT_SPELL_CHECKER_ENCHANT(object)->priv->~WebKitSpellCheckerEnchantPrivate();
71 }
72
73 static void webkit_spell_checker_enchant_class_init(WebKitSpellCheckerEnchantClass* klass)
74 {
75     GObjectClass* objectClass = G_OBJECT_CLASS(klass);
76
77     objectClass->finalize = webkit_spell_checker_enchant_finalize;
78
79     g_type_class_add_private(klass, sizeof(WebKitSpellCheckerEnchantPrivate));
80 }
81
82 static void webkit_spell_checker_enchant_init(WebKitSpellCheckerEnchant* checker)
83 {
84     WebKitSpellCheckerEnchantPrivate* priv = G_TYPE_INSTANCE_GET_PRIVATE(checker, WEBKIT_TYPE_SPELL_CHECKER_ENCHANT, WebKitSpellCheckerEnchantPrivate);
85     checker->priv = priv;
86     new (priv) WebKitSpellCheckerEnchantPrivate();
87
88     priv->enchantDicts = 0;
89 }
90
91 static void checkSpellingOfString(WebKitSpellChecker* checker, const char* string, int* misspellingLocation, int* misspellingLength)
92 {
93     WebKitSpellCheckerEnchantPrivate* priv = WEBKIT_SPELL_CHECKER_ENCHANT(checker)->priv;
94
95     GSList* dicts = priv->enchantDicts;
96     if (!dicts)
97         return;
98
99     int length = g_utf8_strlen(string, -1);
100
101     PangoLanguage* language(pango_language_get_default());
102     GOwnPtr<PangoLogAttr> attrs(g_new(PangoLogAttr, length + 1));
103
104     // pango_get_log_attrs uses an aditional position at the end of the text.
105     pango_get_log_attrs(string, -1, -1, language, attrs.get(), length + 1);
106
107     for (int i = 0; i < length + 1; i++) {
108         // We go through each character until we find an is_word_start,
109         // then we get into an inner loop to find the is_word_end corresponding
110         // to it.
111         if (attrs.get()[i].is_word_start) {
112             int start = i;
113             int end = i;
114             int wordLength;
115
116             while (attrs.get()[end].is_word_end < 1)
117                 end++;
118
119             wordLength = end - start;
120             // Set the iterator to be at the current word end, so we don't
121             // check characters twice.
122             i = end;
123
124             gchar* cstart = g_utf8_offset_to_pointer(string, start);
125             gint bytes = static_cast<gint>(g_utf8_offset_to_pointer(string, end) - cstart);
126             GOwnPtr<gchar> word(g_new0(gchar, bytes + 1));
127
128             g_utf8_strncpy(word.get(), cstart, wordLength);
129
130             for (; dicts; dicts = dicts->next) {
131                 EnchantDict* dict = static_cast<EnchantDict*>(dicts->data);
132                 if (enchant_dict_check(dict, word.get(), wordLength)) {
133                     *misspellingLocation = start;
134                     *misspellingLength = wordLength;
135                 } else {
136                     // Stop checking, this word is ok in at least one dict.
137                     *misspellingLocation = -1;
138                     *misspellingLength = 0;
139                     break;
140                 }
141             }
142         }
143     }
144 }
145
146 static char** getGuessesForWord(WebKitSpellChecker* checker, const char* word, const char* context)
147 {
148     WebKitSpellCheckerEnchantPrivate* priv = WEBKIT_SPELL_CHECKER_ENCHANT(checker)->priv;
149
150     GSList* dicts = priv->enchantDicts;
151     char** guesses = 0;
152
153     for (; dicts; dicts = dicts->next) {
154         size_t numberOfSuggestions;
155         size_t i;
156
157         EnchantDict* dict = static_cast<EnchantDict*>(dicts->data);
158         gchar** suggestions = enchant_dict_suggest(dict, word, -1, &numberOfSuggestions);
159
160         if (numberOfSuggestions > 0) {
161             if (numberOfSuggestions > 10)
162                 numberOfSuggestions = 10;
163
164             guesses = static_cast<char**>(g_malloc0((numberOfSuggestions + 1) * sizeof(char*)));
165             for (i = 0; i < numberOfSuggestions && i < 10; i++)
166                 guesses[i] = g_strdup(suggestions[i]);
167
168             guesses[i] = 0;
169
170             enchant_dict_free_suggestions(dict, suggestions);
171         }
172     }
173
174     return guesses;
175 }
176
177 static void getAvailableDictionariesCallback(const char* const languageTag, const char* const, const char* const, const char* const, void* data)
178 {
179     Vector<CString>* dicts = static_cast<Vector<CString>*>(data);
180
181     dicts->append(languageTag);
182 }
183
184 static void updateSpellCheckingLanguages(WebKitSpellChecker* checker, const char* languages)
185 {
186     GSList* spellDictionaries = 0;
187
188     WebKitSpellCheckerEnchantPrivate* priv = WEBKIT_SPELL_CHECKER_ENCHANT(checker)->priv;
189
190     createEnchantBrokerIfNeeded();
191
192     if (languages) {
193         char** langs = g_strsplit(languages, ",", -1);
194         for (int i = 0; langs[i]; i++) {
195             if (enchant_broker_dict_exists(broker, langs[i])) {
196                 EnchantDict* dict = enchant_broker_request_dict(broker, langs[i]);
197                 spellDictionaries = g_slist_append(spellDictionaries, dict);
198             }
199         }
200         g_strfreev(langs);
201     } else {
202         const char* language = pango_language_to_string(gtk_get_default_language());
203         if (enchant_broker_dict_exists(broker, language)) {
204             EnchantDict* dict = enchant_broker_request_dict(broker, language);
205             spellDictionaries = g_slist_append(spellDictionaries, dict);
206         } else {
207             // No dictionaries selected, we get one from the list.
208             Vector<CString> allDictionaries;
209             enchant_broker_list_dicts(broker, getAvailableDictionariesCallback, &allDictionaries);
210             if (!allDictionaries.isEmpty()) {
211                 EnchantDict* dict = enchant_broker_request_dict(broker, allDictionaries[0].data());
212                 spellDictionaries = g_slist_append(spellDictionaries, dict);
213             }
214         }
215     }
216     g_slist_foreach(priv->enchantDicts, freeSpellCheckingLanguage, 0);
217     g_slist_free(priv->enchantDicts);
218     priv->enchantDicts = spellDictionaries;
219 }
220
221 static char* getAutocorrectSuggestionsForMisspelledWord(WebKitSpellChecker* checker, const char* word)
222 {
223     return 0;
224 }
225
226 static void learnWord(WebKitSpellChecker* checker, const char* word)
227 {
228     WebKitSpellCheckerEnchantPrivate* priv = WEBKIT_SPELL_CHECKER_ENCHANT(checker)->priv;
229     GSList* dicts = priv->enchantDicts;
230
231     for (; dicts; dicts = dicts->next) {
232         EnchantDict* dict = static_cast<EnchantDict*>(dicts->data);
233
234         enchant_dict_add_to_personal(dict, word, -1);
235     }
236 }
237
238 static void ignoreWord(WebKitSpellChecker* checker, const char* word)
239 {
240     WebKitSpellCheckerEnchantPrivate* priv = WEBKIT_SPELL_CHECKER_ENCHANT(checker)->priv;
241     GSList* dicts = priv->enchantDicts;
242
243     for (; dicts; dicts = dicts->next) {
244         EnchantDict* dict = static_cast<EnchantDict*>(dicts->data);
245
246         enchant_dict_add_to_session(dict, word, -1);
247     }
248 }
249
250 static void webkit_spell_checker_enchant_spell_checker_interface_init(WebKitSpellCheckerInterface* interface)
251 {
252     interface->check_spelling_of_string = checkSpellingOfString;
253     interface->get_guesses_for_word = getGuessesForWord;
254     interface->update_spell_checking_languages = updateSpellCheckingLanguages;
255     interface->get_autocorrect_suggestions_for_misspelled_word = getAutocorrectSuggestionsForMisspelledWord;
256     interface->learn_word = learnWord;
257     interface->ignore_word = ignoreWord;
258 }
259
260 #endif /* ENABLE(SPELLCHECK) */
261