• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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