Implement LFUN_SPELLING_REMOVE (patch from switt)

git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@35055 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
Jean-Marc Lasgouttes 2010-08-05 20:10:40 +00:00
parent f5e2d78792
commit e4f2484cb5
19 changed files with 186 additions and 69 deletions

View 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").

View 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() << "\"") ;
}

View 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();

View 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;
}

View 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();

View 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;
}

View 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;
}

View 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();

View 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

View 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?

View 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();

View 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.

View 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) ;
}

View 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.

View 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;

View 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;

View 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 {

View File

@ -16,16 +16,24 @@
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

View 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) {