]> git.lyx.org Git - lyx.git/commitdiff
Implement LFUN_SPELLING_REMOVE (patch from switt)
authorJean-Marc Lasgouttes <lasgouttes@lyx.org>
Thu, 5 Aug 2010 20:10:40 +0000 (20:10 +0000)
committerJean-Marc Lasgouttes <lasgouttes@lyx.org>
Thu, 5 Aug 2010 20:10:40 +0000 (20:10 +0000)
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@35055 a592a061-630c-0410-9148-cb99ea01b6c8

19 files changed:
RELEASE-NOTES
src/AppleSpellChecker.cpp
src/AppleSpellChecker.h
src/AspellChecker.cpp
src/AspellChecker.h
src/Buffer.cpp
src/EnchantChecker.cpp
src/EnchantChecker.h
src/FuncCode.h
src/HunspellChecker.cpp
src/HunspellChecker.h
src/LyXAction.cpp
src/Paragraph.cpp
src/Paragraph.h
src/SpellChecker.h
src/Text3.cpp
src/frontends/qt4/Menus.cpp
src/support/AppleSpeller.h
src/support/AppleSpeller.m

index 3cf7179e2ced394d1933eb23774319ebe9d3221d..dfe50f3d2bf59eaf480930d75b7bd52d8c14967c 100644 (file)
@@ -84,6 +84,8 @@ The following new LyX functions have been introduced:
 
 - LFUN_SPELLING_IGNORE ("spelling-ignore").
 
+- LFUN_SPELLING_REMOVE ("spelling-remove").
+
 - LFUN_PREVIEW_INSERT ("preview-insert").
 
 - LFUN_FORWARD_SEARCH ("forward-search").
index 2beadb2eae723aa0be8569fe36f7068403f477a4..003b8051ec83849c87251dda31b48be6036a74e4 100644 (file)
@@ -29,6 +29,9 @@ struct AppleSpellChecker::Private
 
        ~Private();
 
+       SpellChecker::Result toResult(SpellCheckResult status);
+       string toString(SpellCheckResult status);
+
        /// the speller
        AppleSpeller speller;
 };
@@ -58,11 +61,26 @@ AppleSpellChecker::~AppleSpellChecker()
 }
 
 
+SpellChecker::Result AppleSpellChecker::Private::toResult(SpellCheckResult status)
+{
+       return status == SPELL_CHECK_FAILED ? UNKNOWN_WORD :
+               status == SPELL_CHECK_LEARNED ? LEARNED_WORD : WORD_OK ;
+}
+
+
+string AppleSpellChecker::Private::toString(SpellCheckResult status)
+{
+       return status == SPELL_CHECK_FAILED ? "FAILED" :
+                status == SPELL_CHECK_LEARNED ? "LEARNED" : "OK";
+}
+
+
 SpellChecker::Result AppleSpellChecker::check(WordLangTuple const & word)
 {
        string const word_str = to_utf8(word.word());
-       int const word_ok = checkAppleSpeller(d->speller, word_str.c_str(), word.lang()->code().c_str());
-       return (word_ok) ? OK : UNKNOWN_WORD;
+       SpellCheckResult result = checkAppleSpeller(d->speller, word_str.c_str(), word.lang()->code().c_str());
+       LYXERR(Debug::GUI, "spellCheck: \"" << word.word() << "\" = " << d->toString(result)) ;
+       return d->toResult(result);
 }
 
 
@@ -71,6 +89,16 @@ void AppleSpellChecker::insert(WordLangTuple const & word)
 {
        string const word_str = to_utf8(word.word());
        learnAppleSpeller(d->speller, word_str.c_str());
+       LYXERR(Debug::GUI, "learn word: \"" << word.word() << "\"") ;
+}
+
+
+// remove from personal dictionary
+void AppleSpellChecker::remove(WordLangTuple const & word)
+{
+       string const word_str = to_utf8(word.word());
+       unlearnAppleSpeller(d->speller, word_str.c_str());
+       LYXERR(Debug::GUI, "unlearn word: \"" << word.word() << "\"") ;
 }
 
 
index 5a6657520c174aea6511bfa2bad0081b112700b0..e406325a706256f69ab27e7eecbbddd0b6f3cb4f 100644 (file)
@@ -27,6 +27,7 @@ public:
        enum Result check(WordLangTuple const &);
        void suggest(WordLangTuple const &, docstring_list &);
        void insert(WordLangTuple const &);
+       void remove(WordLangTuple const &);
        void accept(WordLangTuple const &);
        bool hasDictionary(Language const * lang) const;
        docstring const error();
index 36e8e04c8f23375ba608b61261a4e16a4328bd70..06cc6bf49a123c40a0ea7d8c5904d9536f711ead 100644 (file)
@@ -266,17 +266,17 @@ SpellChecker::Result AspellChecker::check(WordLangTuple const & word)
                d->speller(word.lang()->code(), word.lang()->variety());
 
        if (!m)
-               return OK;
+               return WORD_OK;
 
        if (word.word().empty())
                // MSVC compiled Aspell doesn't like it.
-               return OK;
+               return WORD_OK;
 
        string const word_str = to_utf8(word.word());
        int const word_ok = aspell_speller_check(m, word_str.c_str(), -1);
        LASSERT(word_ok != -1, /**/);
 
-       return (word_ok) ? OK : UNKNOWN_WORD;
+       return (word_ok) ? WORD_OK : UNKNOWN_WORD;
 }
 
 
index 73a685a4572733b55b115125fafa40d2657a38a6..6bef6a854a0a6f9396dd8ae307636657f7a5c9b0 100644 (file)
@@ -28,6 +28,7 @@ public:
        enum Result check(WordLangTuple const &);
        void suggest(WordLangTuple const &, docstring_list &);
        void insert(WordLangTuple const &);
+       void remove(WordLangTuple const &) {};
        void accept(WordLangTuple const &);
        bool hasDictionary(Language const * lang) const;
        docstring const error();
index ba77061463c56c7176f0dbf685adb2d8172648e1..9063b0ed233d2b1879f722a5c256facb75a28d8e 100644 (file)
@@ -3995,7 +3995,8 @@ int Buffer::spellCheck(DocIterator & from, DocIterator & to,
                if (from == end)
                        break;
                to = from;
-               if (from.paragraph().spellCheck(from.pos(), to.pos(), wl, suggestions)) {
+               SpellChecker::Result res = from.paragraph().spellCheck(from.pos(), to.pos(), wl, suggestions);
+               if (SpellChecker::misspelled(res)) {
                        word_lang = wl;
                        break;
                }
index 3f3b138f58a0d36a7f27f36758b4b4ca515832e8..793bc16fb95da8499743b6c8bc4fc26645ff3ee7 100644 (file)
@@ -113,12 +113,12 @@ SpellChecker::Result EnchantChecker::check(WordLangTuple const & word)
        enchant::Dict * m = d->speller(word.lang()->code());
 
        if (!m)
-               return OK;
+               return WORD_OK;
 
        string utf8word = to_utf8(word.word());
 
        if (m->check(utf8word))
-               return OK;
+               return WORD_OK;
 
        return UNKNOWN_WORD;
 }
index 94f7e20b38fb82797b5639f1a17d29dfd2df2c0c..5016ed3d7bdbeb10ff3a6f9307c27402356b265e 100644 (file)
@@ -34,6 +34,7 @@ public:
        enum Result check(WordLangTuple const &);
        void suggest(WordLangTuple const &, docstring_list &);
        void insert(WordLangTuple const &);
+       void remove(WordLangTuple const &) {};
        void accept(WordLangTuple const &);
        bool hasDictionary(Language const * lang) const;
        docstring const error();
index 4b5d94a84770e48848b67cc57a028ff60e411829..8867ce1c7a170800766bd338e4612ff912d05ed3 100644 (file)
@@ -444,6 +444,7 @@ enum FuncCode
        LFUN_SPELLING_ADD,              // spitz 20100118
        LFUN_SPELLING_IGNORE,           // spitz 20100118
        // 345
+       LFUN_SPELLING_REMOVE,           // switt 20100728
        LFUN_PREVIEW_INSERT,            // vfr, 20100328
        LFUN_FORWARD_SEARCH,
        LFUN_INSET_COPY_AS,             // vfr, 20100419
index f5b9d0c61a88158662e9af684042eca3b9b59ceb..f3b8c25edb83bcd53589bb2aef2680873c941e1d 100644 (file)
@@ -207,18 +207,18 @@ HunspellChecker::~HunspellChecker()
 SpellChecker::Result HunspellChecker::check(WordLangTuple const & wl)
 {
        if (d->isIgnored(wl))
-               return OK;
+               return WORD_OK;
 
        Hunspell * h = d->speller(wl.lang()->code());
        if (!h)
-               return OK;
+               return WORD_OK;
        int info;
 
        string const encoding = h->get_dic_encoding();
        string const word_to_check = to_iconv_encoding(wl.word(), encoding);
        
        if (h->spell(word_to_check.c_str(), &info))
-               return OK;
+               return WORD_OK;
 
        if (info & SPELL_COMPOUND) {
                // FIXME: What to do with that?
index ccb5257be5bea4b1d6dee943b09d320683f2c15b..f5daaf3c436ebc3f90a4c90a562025f59d58354c 100644 (file)
@@ -28,6 +28,7 @@ public:
        enum Result check(WordLangTuple const &);
        void suggest(WordLangTuple const &, docstring_list &);
        void insert(WordLangTuple const &);
+       void remove(WordLangTuple const &) {};
        void accept(WordLangTuple const &);
        bool hasDictionary(Language const * lang) const;
        docstring const error();
index a731ead1cef01ff69bef8ba45a4165b13d52b773..7ec036dec45eed5a3c39541e3cf27a133d8e1b3a 100644 (file)
@@ -1015,6 +1015,17 @@ void LyXAction::init()
  * \endvar
  */
                { LFUN_SPELLING_IGNORE, "spelling-ignore", ReadOnly, Edit },
+/*!
+ * \var lyx::FuncCode lyx::LFUN_SPELLING_REMOVE
+ * \li Action: Remove the word under the cursor from the respective
+ *             spell checker dictionary.
+ * \li Syntax: spelling-remove [<STRING>] [<LANG>]
+ * \li Params: <WORD>: word to remove
+               <LANG>: language name (see file languages)
+ * \li Origin: SWitt, 28 July 2010
+ * \endvar
+ */
+               { LFUN_SPELLING_REMOVE, "spelling-remove", ReadOnly, Edit },
 /*!
  * \var lyx::FuncCode lyx::LFUN_THESAURUS_ENTRY
  * \li Action: Look up thesaurus entries with respect to the word under the cursor.
index 150c28d6a9adafe53b8be2cdc7d224dbf3d297e9..cfbb73e1b69fc140a7ab212eb327b065b5efbdb5 100644 (file)
@@ -3169,19 +3169,19 @@ void Paragraph::updateWords()
 }
 
 
-bool Paragraph::spellCheck(pos_type & from, pos_type & to, WordLangTuple & wl,
+SpellChecker::Result Paragraph::spellCheck(pos_type & from, pos_type & to, WordLangTuple & wl,
        docstring_list & suggestions, bool do_suggestion) const
 {
        SpellChecker * speller = theSpellChecker();
        if (!speller)
-               return false;
+               return SpellChecker::WORD_OK;
 
        if (!d->layout_->spellcheck || !inInset().allowSpellCheck())
-               return false;
+               return SpellChecker::WORD_OK;
 
        locateWord(from, to, WHOLE_WORD);
        if (from == to || from >= pos_type(d->text_.size()))
-               return false;
+               return SpellChecker::WORD_OK;
 
        docstring word = asString(from, to, AS_STR_INSETS);
        // Ignore words with digits
@@ -3200,29 +3200,19 @@ bool Paragraph::spellCheck(pos_type & from, pos_type & to, WordLangTuple & wl,
        }
        wl = WordLangTuple(word, lang);
        SpellChecker::Result res = ignored ?
-               SpellChecker::OK : speller->check(wl);
-#if 0
-// FIXME: the code below makes aspell abort if a word in an unknown
-//       language is checked.
-       // Just ignore any error that the spellchecker reports.
-       // FIXME: we should through out an exception and catch it in the GUI to
-       // display the error.
-       if (!speller->error().empty())
-               return false;
-#endif
+               SpellChecker::WORD_OK : speller->check(wl);
 
-       bool const misspelled = res != SpellChecker::OK
-               && res != SpellChecker::IGNORED_WORD;
+       bool const misspelled_ = SpellChecker::misspelled(res) ;
 
        if (lyxrc.spellcheck_continuously)
-               d->fontlist_.setMisspelled(from, to, misspelled);
+               d->fontlist_.setMisspelled(from, to, misspelled_);
 
-       if (misspelled && do_suggestion)
+       if (misspelled_ && do_suggestion)
                speller->suggest(wl, suggestions);
        else
                suggestions.clear();
 
-       return misspelled;
+       return res;
 }
 
 
@@ -3232,7 +3222,8 @@ bool Paragraph::isMisspelled(pos_type pos) const
        pos_type to = pos;
        WordLangTuple wl;
        docstring_list suggestions;
-       return spellCheck(from, to, wl, suggestions, false);
+       SpellChecker::Result res = spellCheck(from, to, wl, suggestions, false);
+       return SpellChecker::misspelled(res) ;
 }
 
 
index 65c0190d62ba1185a5b98e92f648179a281ba034..26389c6ced7e99529523c63b3b83c38c31ca5607 100644 (file)
@@ -17,6 +17,7 @@
 #define PARAGRAPH_H
 
 #include "FontEnums.h"
+#include "SpellChecker.h"
 
 #include "insets/InsetCode.h"
 
@@ -422,8 +423,8 @@ public:
 
        /// Spellcheck word at position \p from and fill in found misspelled word
        /// and \p suggestions if \p do_suggestion is true.
-       /// \return true if pointed word is misspelled.
-       bool spellCheck(pos_type & from, pos_type & to, WordLangTuple & wl,
+       /// \return result from spell checker, SpellChecker::UNKNOWN_WORD when misspelled.
+       SpellChecker::Result spellCheck(pos_type & from, pos_type & to, WordLangTuple & wl,
                docstring_list & suggestions, bool do_suggestion =  true) const;
 
        /// Spellcheck word at position \p pos.
index bac19d116104f39b12af4fc3c0dc5553ed4d415c..58a1fb2032ba476d9fc3734458d6db8ba73c54b0 100644 (file)
@@ -32,19 +32,27 @@ public:
        /// the result from checking a single word
        enum Result  {
                /// word is correct
-               OK = 1,
+               WORD_OK = 1,
                /// root of given word was found
-               ROOT,
+               ROOT_FOUND,
                /// word found through compound formation
                COMPOUND_WORD,
                /// word not found
                UNKNOWN_WORD,
                /// number of other ignored "word"
-               IGNORED_WORD
+               IGNORED_WORD,
+               /// number of personal dictionary "word"
+               LEARNED_WORD
        };
 
        virtual ~SpellChecker() {}
 
+       /// does the spell check failed
+       static bool misspelled(Result res) {
+               return res != SpellChecker::WORD_OK
+                       && res != SpellChecker::IGNORED_WORD
+                       && res != SpellChecker::LEARNED_WORD; }
+
        /// check the given word of the given lang code and return the result
        virtual enum Result check(WordLangTuple const &) = 0;
        
@@ -54,6 +62,9 @@ public:
        /// insert the given word into the personal dictionary
        virtual void insert(WordLangTuple const &) = 0;
 
+       /// remove the given word from the personal dictionary
+       virtual void remove(WordLangTuple const &) = 0;
+
        /// accept the given word temporarily
        virtual void accept(WordLangTuple const &) = 0;
 
index a871a62d1b979e0b4868b7488f6d217fe8f4cfaf..40fff5dbe5cfbb92aa4e14dbef65b714deef4f0c 100644 (file)
@@ -2044,6 +2044,25 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
                break;
        }
 
+       case LFUN_SPELLING_REMOVE: {
+               docstring word = from_utf8(cmd.getArg(0));
+               Language * lang;
+               if (word.empty()) {
+                       word = cur.selectionAsString(false);
+                       // FIXME
+                       if (word.size() > 100 || word.empty()) {
+                               // Get word or selection
+                               selectWordWhenUnderCursor(cur, WHOLE_WORD);
+                               word = cur.selectionAsString(false);
+                       }
+                       lang = const_cast<Language *>(cur.getFont().language());
+               } else
+                       lang = const_cast<Language *>(languages.getLanguage(cmd.getArg(1)));
+               WordLangTuple wl(word, lang);
+               theSpellChecker()->remove(wl);
+               break;
+       }
+
        case LFUN_PARAGRAPH_PARAMS_APPLY: {
                // Given data, an encoding of the ParagraphParameters
                // generated in the Paragraph dialog, this function sets
@@ -2591,6 +2610,7 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd,
        
        case LFUN_SPELLING_ADD:
        case LFUN_SPELLING_IGNORE:
+       case LFUN_SPELLING_REMOVE:
                enable = theSpellChecker();
                break;
 
index 5e4665499e2ce717d76173cbee5b87fb0175d328..2abf282e7ce54afc82e94aa68916aa24e8aa868a 100644 (file)
@@ -46,6 +46,7 @@
 #include "Paragraph.h"
 #include "ParIterator.h"
 #include "Session.h"
+#include "SpellChecker.h"
 #include "TextClass.h"
 #include "TocBackend.h"
 #include "Toolbars.h"
@@ -730,39 +731,55 @@ void MenuDefinition::expandGraphicsGroups(BufferView const * bv)
 
 void MenuDefinition::expandSpellingSuggestions(BufferView const * bv)
 {
-       if (!bv || !lyxrc.spellcheck_continuously)
+       if (!bv)
                return;
        WordLangTuple wl;
        docstring_list suggestions;
        pos_type from = bv->cursor().pos();
        pos_type to = from;
        Paragraph const & par = bv->cursor().paragraph();
-       if (!par.spellCheck(from, to, wl, suggestions))
-               return;
-       LYXERR(Debug::GUI, "Misspelled Word! Suggested Words = ");
-       size_t i = 0;
-       MenuItem item(MenuItem::Submenu, qt_("More Spelling Suggestions"));
-       item.setSubmenu(MenuDefinition(qt_("More Spelling Suggestions")));
-       for (; i != suggestions.size(); ++i) {
-               docstring const & suggestion = suggestions[i];
-               LYXERR(Debug::GUI, suggestion);
-               MenuItem w(MenuItem::Command, toqstr(suggestion),
-                       FuncRequest(LFUN_WORD_REPLACE, suggestion));
-               if (i < 10)
-                       add(w);
-               else
-                       item.submenu().add(w);
+       SpellChecker::Result res = par.spellCheck(from, to, wl, suggestions);
+       switch (res) {
+       case SpellChecker::UNKNOWN_WORD:
+               if (lyxrc.spellcheck_continuously) {
+                       LYXERR(Debug::GUI, "Misspelled Word! Suggested Words = ");
+                       size_t i = 0;
+                       MenuItem item(MenuItem::Submenu, qt_("More Spelling Suggestions"));
+                       item.setSubmenu(MenuDefinition(qt_("More Spelling Suggestions")));
+                       for (; i != suggestions.size(); ++i) {
+                               docstring const & suggestion = suggestions[i];
+                               LYXERR(Debug::GUI, suggestion);
+                               MenuItem w(MenuItem::Command, toqstr(suggestion),
+                                       FuncRequest(LFUN_WORD_REPLACE, suggestion));
+                               if (i < 10)
+                                       add(w);
+                               else
+                                       item.submenu().add(w);
+                       }
+                       if (i >= 10)
+                               add(item);
+                       if (i > 0)
+                               add(MenuItem(MenuItem::Separator));
+                       docstring const arg = wl.word() + " " + from_ascii(wl.lang()->lang());
+                       add(MenuItem(MenuItem::Command, qt_("Add to personal dictionary|c"),
+                                       FuncRequest(LFUN_SPELLING_ADD, arg)));
+                       add(MenuItem(MenuItem::Command, qt_("Ignore all|I"),
+                                       FuncRequest(LFUN_SPELLING_IGNORE, arg)));
+               }
+               break;
+       case SpellChecker::LEARNED_WORD: {
+                       LYXERR(Debug::GUI, "Learned Word.");
+                       docstring const arg = wl.word() + " " + from_ascii(wl.lang()->lang());
+                       add(MenuItem(MenuItem::Command, qt_("Remove from personal dictionary|r"),
+                                       FuncRequest(LFUN_SPELLING_REMOVE, arg)));
+               }
+               break;
+       case SpellChecker::WORD_OK:
+       case SpellChecker::COMPOUND_WORD:
+       case SpellChecker::ROOT_FOUND:
+       case SpellChecker::IGNORED_WORD:
+               break;
        }
-       if (i >= 10)
-               add(item);
-       if (i > 0)
-               add(MenuItem(MenuItem::Separator));
-       docstring const arg = wl.word() + " " + from_ascii(wl.lang()->lang());
-       add(MenuItem(MenuItem::Command, qt_("Add to personal dictionary|c"),
-                       FuncRequest(LFUN_SPELLING_ADD, arg)));
-       add(MenuItem(MenuItem::Command, qt_("Ignore all|I"),
-                       FuncRequest(LFUN_SPELLING_IGNORE, arg)));
-       
 }
 
 struct sortLanguageByName {
index 3ff3ea19adc7cfee1bc8be7a786967eaee265fe0..3012249410b93f0f5fcc939c4fe42137b41d451a 100644 (file)
 extern "C" {
 #endif
 
+typedef enum SpellCheckResult {
+       SPELL_CHECK_FAILED,
+       SPELL_CHECK_OK,
+       SPELL_CHECK_IGNORED,
+       SPELL_CHECK_LEARNED
+} SpellCheckResult ;
+
 typedef struct AppleSpellerRec * AppleSpeller ;
 
 AppleSpeller newAppleSpeller(void);
 void freeAppleSpeller(AppleSpeller speller);
 
-int checkAppleSpeller(AppleSpeller speller, const char * word, const char * lang);
+SpellCheckResult checkAppleSpeller(AppleSpeller speller, const char * word, const char * lang);
 void ignoreAppleSpeller(AppleSpeller speller, const char * word);
 size_t makeSuggestionAppleSpeller(AppleSpeller speller, const char * word, const char * lang);
 const char * getSuggestionAppleSpeller(AppleSpeller speller, size_t pos);
 void learnAppleSpeller(AppleSpeller speller, const char * word);
+void unlearnAppleSpeller(AppleSpeller speller, const char * word);
 int hasLanguageAppleSpeller(AppleSpeller speller, const char * lang);
 
 #ifdef __cplusplus
index 24e9cc5c82daa0437a7a4cd9fb84dffb29248671..37a361b453311bf9a0c12be71352a3077aca92f9 100644 (file)
@@ -68,16 +68,16 @@ static NSString * toString(const char * word)
 }
 
 
-int checkAppleSpeller(AppleSpeller speller, const char * word, const char * lang)
+SpellCheckResult checkAppleSpeller(AppleSpeller speller, const char * word, const char * lang)
 {
        if (!speller->checker || !lang || !word)
-               return 0;
+               return SPELL_CHECK_FAILED;
 
        NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
        NSString * word_ = toString(word);
        NSString * lang_ = toString(lang);
 
-       NSRange result = [speller->checker
+       NSRange match = [speller->checker
                checkSpellingOfString:word_
                startingAt:0
                language:lang_
@@ -85,11 +85,17 @@ int checkAppleSpeller(AppleSpeller speller, const char * word, const char * lang
                inSpellDocumentWithTag:speller->doctag
                wordCount:NULL];
 
+       SpellCheckResult result = match.length == 0 ? SPELL_CHECK_OK : SPELL_CHECK_FAILED;
+       if (result == SPELL_CHECK_OK && [NSSpellChecker instancesRespondToSelector:@selector(hasLearnedWord:)]) {
+               if ([speller->checker hasLearnedWord:word_])
+                       result = SPELL_CHECK_LEARNED;
+       }
+
        [word_ release];
        [lang_ release];
        [pool release];
 
-       return (result.length ? 0 : 1);
+       return result;
 }
 
 
@@ -172,7 +178,7 @@ void learnAppleSpeller(AppleSpeller speller, const char * word)
        NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
        NSString * word_ = toString(word);
 
-       if ([NSSpellChecker instancesRespondToSelector:@selector(learnWord)])
+       if ([NSSpellChecker instancesRespondToSelector:@selector(learnWord:)])
                [speller->checker learnWord:word_];
        
        [word_ release];
@@ -181,13 +187,29 @@ void learnAppleSpeller(AppleSpeller speller, const char * word)
 }
 
 
+
+void unlearnAppleSpeller(AppleSpeller speller, const char * word)
+{
+#if defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1050)
+       NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
+       NSString * word_ = toString(word);
+
+       if ([NSSpellChecker instancesRespondToSelector:@selector(unlearnWord:)])
+               [speller->checker unlearnWord:word_];
+       
+       [word_ release];
+       [pool release];
+#endif
+}
+
+
 int hasLanguageAppleSpeller(AppleSpeller speller, const char * lang)
 {
        BOOL result = NO;
 #if defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1050)
        NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
        NSString * lang_ = toString(lang);
-       if ([NSSpellChecker instancesRespondToSelector:@selector(availableLanguages)]) {
+       if ([NSSpellChecker instancesRespondToSelector:@selector(availableLanguages:)]) {
                NSArray * languages = [speller->checker availableLanguages];
 
                for (NSString *element in languages) {