Home | History | Annotate | Download | only in WebCoreSupport
      1 /*
      2  *  Copyright (C) 2007 Alp Toker <alp (at) atoker.com>
      3  *  Copyright (C) 2008 Nuanti Ltd.
      4  *  Copyright (C) 2009 Diego Escalante Urrelo <diegoe (at) 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 (at) 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 
     40 TextCheckerClientEnchant::TextCheckerClientEnchant(WebKitWebView* webView)
     41     : m_webView(webView)
     42     , m_enchantDicts(0)
     43 {
     44 }
     45 
     46 TextCheckerClientEnchant::~TextCheckerClientEnchant()
     47 {
     48     g_slist_foreach(m_enchantDicts, freeSpellCheckingLanguage, 0);
     49     g_slist_free(m_enchantDicts);
     50 }
     51 
     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 
     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 
     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 
    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 
    135 void TextCheckerClientEnchant::checkGrammarOfString(const UChar*, int, Vector<GrammarDetail>&, int*, int*)
    136 {
    137     notImplemented();
    138 }
    139 
    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 
    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 
    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 
    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