provide native spell checker for Mac OS X

git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@34801 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
Stephan Witt 2010-07-07 17:40:50 +00:00
parent 4da0c94afa
commit 0a326e90f7
9 changed files with 512 additions and 12 deletions

View File

@ -199,7 +199,7 @@ HostSystem_ppc="powerpc-apple-darwin8"
QtLibraries="QtSvg QtXml QtGui QtNetwork QtCore"
DMGNAME="${LyxBase}"
DMGSIZE="350m"
DMGSIZE="550m"
BACKGROUND="${LyxAppDir}.app/Contents/Resources/images/banner.png"
# Check for existing SDKs
@ -228,7 +228,91 @@ case "$SDKs" in
exit 1
;;
esac
MYCFLAGS="-mmacosx-version-min=${MACOSX_DEPLOYMENT_TARGET}"
case "${MACOSX_DEPLOYMENT_TARGET}" in
10.4)
MYCFLAGS="-DLYX_PLATFORM_DARWIN10=4"
;;
10.5)
MYCFLAGS="-DLYX_PLATFORM_DARWIN10=5"
;;
10.6)
MYCFLAGS="-DLYX_PLATFORM_DARWIN10=6"
;;
esac
MYCFLAGS="$MYCFLAGS -mmacosx-version-min=${MACOSX_DEPLOYMENT_TARGET}"
updateDictionaries() {
TMP_DIR="/tmp/lyx-build-$$"
mkdir -p "$1"/dict "$1"/thes
mkdir -p "$TMP_DIR" && (
for pack in "$1"/*.zip ; do
case "${pack}" in
*de_DE-pack.zip)
cd "$TMP_DIR" && unzip "${pack}" de_DE_comb.zip thes_de_DE_v2.zip
cd "$1"/dict && unzip -o "$TMP_DIR"/de_DE_comb.zip
cd "$1"/thes && unzip -o "$TMP_DIR"/thes_de_DE_v2.zip
;;
*pl_PL-pack.zip)
cd "$TMP_DIR" && unzip "${pack}" pl_PL.zip thes_pl_PL_v2.zip
cd "$1"/dict && unzip -o "$TMP_DIR"/pl_PL.zip
cd "$1"/thes && unzip -o "$TMP_DIR"/thes_pl_PL_v2.zip
;;
*fr_FR-pack.zip)
cd "$TMP_DIR" && unzip "${pack}" fr_FR.zip thes_fr_FR_v2.zip
cd "$1"/dict && unzip -o "$TMP_DIR"/fr_FR.zip
cd "$1"/thes && unzip -o "$TMP_DIR"/thes_fr_FR_v2.zip
;;
*es_ES-pack.zip)
cd "$TMP_DIR" && unzip "${pack}" es_ES.zip es_MX.zip thes_es_ES_v2.zip
cd "$1"/dict && unzip -o "$TMP_DIR"/es_ES.zip
cd "$1"/dict && unzip -o "$TMP_DIR"/es_MX.zip
cd "$1"/thes && unzip -o "$TMP_DIR"/thes_es_ES_v2.zip
;;
*pt_PT-pack.zip)
cd "$TMP_DIR" && unzip "${pack}" pt_PT.zip
cd "$1"/dict && unzip -o "$TMP_DIR"/pt_PT.zip
cd "$1"/dict && unzip -o "$1"/pt_BR.zip
cd "$1"/thes && unzip -o "$1"/thes_pt_PT_v2.zip
;;
*it_IT-pack.zip)
cd "$TMP_DIR" && unzip "${pack}" it_IT.zip
cd "$1"/dict && unzip -o "$TMP_DIR"/it_IT.zip
cd "$1"/thes && unzip -o "$1"/thes_it_IT_v2.zip
;;
*ru_RU-pack.zip)
cd "$TMP_DIR" && unzip "${pack}" ru_RU.zip
cd "$1"/dict && unzip -o "$TMP_DIR"/ru_RU.zip
cd "$1"/thes && tar xvf "$1"/thes_ru_RU_v2.tar.bz2
;;
*en_EN-pack.zip)
cd "$TMP_DIR" && unzip "${pack}" en_AU.zip en_CA.zip en_GB.zip en_NZ.zip en_US.zip
for zipfile in en_AU.zip en_CA.zip en_GB.zip en_NZ.zip en_US.zip ; do
( cd "$1"/dict && unzip -o "$TMP_DIR/$zipfile" )
done
cd "$1"/thes && unzip -o "$1"/thes_en_US_v2.zip
;;
XXXX*-pack*)
cd "$TMP_DIR" && unzip -l "${pack}" | while read len date time zipfile ; do
case "$zipfile" in
thes*_v2.zip)
echo "$zipfile"
cd "$TMP_DIR" && unzip -o "${pack}" "$zipfile"
cd "$1"/thes && unzip -o "$TMP_DIR"/"$zipfile"
;;
[a-z][a-z]_[A-Z][A-Z].zip)
echo "$zipfile"
cd "$TMP_DIR" && unzip -o "${pack}" "$zipfile"
cd "$1"/dict && unzip -o "$TMP_DIR"/"$zipfile"
;;
esac
done
# echo Ignore dictionary package `basename "${pack}"`
;;
esac
done
)
rm -rf "$TMP_DIR"
}
if [ -d "${Qt4SourceDir}" -a ! -d "${Qt4BuildDir}" ]; then
echo Build Qt4 library ${Qt4SourceDir}
@ -253,6 +337,9 @@ if [ -d "${Qt4SourceDir}" -a ! -d "${Qt4BuildDir}" ]; then
)
fi
# updateDictionaries "${DictionarySourceDir}"
# exit
if [ -d "${HunSpellSourceDir}" -a ! -f "${HunSpellInstallHdr}" ]; then
# we have a private HunSpell source tree at hand...
# so let's build and install it
@ -317,9 +404,6 @@ if [ -d "${HunSpellSourceDir}" -a ! -f "${HunSpellInstallHdr}" ]; then
done
fi
#exit 0
if [ -d "${ASpellSourceDir}" -a ! -f "${ASpellInstallHdr}" -a "yes" = "${aspell_deployment}" ]; then
# we have a private ASpell source tree at hand...
# so let's build and install it
@ -383,11 +467,14 @@ if [ -d "${ASpellSourceDir}" -a ! -f "${ASpellInstallHdr}" -a "yes" = "${aspell_
done
fi
# exit 0
framework_name() {
echo "Frameworks/${1}.framework"
}
if [ ! -f "${LyxSourceDir}"/configure ]; then
if [ ! -f "${LyxSourceDir}"/configure -o "${LyxSourceDir}"/configure -ot "${LyxSourceDir}"/configure.ac ]; then
( cd "${LyxSourceDir}" && sh autogen.sh )
fi
@ -565,18 +652,19 @@ convert_universal() {
copy_dictionaries() {
if [ -d "${ASpellInstallDir}" -a "yes" = "${aspell_deployment}" ]; then
ASpellFramework=`framework_name Aspell`
ASpellResources="${LyxAppPrefix}/Contents/${ASpellFramework}/Resources"
ASpellResources="${LyxAppPrefix}/Contents/Resources"
# try to reuse macports dictionaries for now
if [ -d /opt/local/lib/aspell-0.60 ]; then ASpellInstallDir=/opt/local ; fi
mkdir -p "${ASpellResources}"
echo Copy Aspell dictionaries from "${ASpellInstallDir}"
cp -p -r "${ASpellInstallDir}/lib/aspell-0.60" "${ASpellResources}"/data
cp -p -r "${ASpellInstallDir}/share/aspell" "${ASpellResources}"/dict
mkdir -p "${ASpellResources}"/data "${ASpellResources}"/dict
cp -p -r "${ASpellInstallDir}/lib/aspell-0.60"/* "${ASpellResources}"/data
cp -p -r "${ASpellInstallDir}/share/aspell"/* "${ASpellResources}"/dict
fi
if [ -d "${HunSpellInstallDir}" -a "yes" = "${hunspell_deployment}" ]; then
HunSpellResources="${LyxAppPrefix}/Contents/Resources"
if [ -d "${DictionarySourceDir}" ]; then
updateDictionaries "${DictionarySourceDir}"
cp -p -r "${DictionarySourceDir}/dict" "${HunSpellResources}"
fi
fi

View File

@ -9,6 +9,9 @@ project(support)
file(GLOB support_sources ${TOP_SRC_DIR}/src/support/${LYX_CPP_FILES})
file(GLOB moc_files ${TOP_SRC_DIR}/src/support/${LYX_MOC_FILES})
list(REMOVE_ITEM support_sources ${moc_files} .)
if(APPLE)
list(APPEND support_sources ${TOP_SRC_DIR}/src/support/AppleSpeller.m)
endif()
file(GLOB support_headers ${TOP_SRC_DIR}/src/support/${LYX_HPP_FILES})

111
src/AppleSpellChecker.cpp Normal file
View File

@ -0,0 +1,111 @@
/**
* \file AppleSpellChecker.cpp
* This file is part of LyX, the document processor.
* Licence details can be found in the file COPYING.
*
* \author Stephan Witt
*
* Full author contact details are available in file CREDITS.
*/
#include <config.h>
#include "AppleSpellChecker.h"
#include "WordLangTuple.h"
#include "support/lassert.h"
#include "support/debug.h"
#include "support/docstring_list.h"
#include "support/AppleSpeller.h"
using namespace std;
using namespace lyx::support;
namespace lyx {
struct AppleSpellChecker::Private
{
Private();
~Private();
/// the speller
AppleSpeller speller;
};
AppleSpellChecker::Private::Private()
{
speller = newAppleSpeller();
}
AppleSpellChecker::Private::~Private()
{
freeAppleSpeller(speller);
speller = 0;
}
AppleSpellChecker::AppleSpellChecker(): d(new Private)
{
}
AppleSpellChecker::~AppleSpellChecker()
{
delete d;
}
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;
}
// add to personal dictionary
void AppleSpellChecker::insert(WordLangTuple const & word)
{
string const word_str = to_utf8(word.word());
learnAppleSpeller(d->speller, word_str.c_str(), word.lang()->code().c_str());
}
// ignore for session
void AppleSpellChecker::accept(WordLangTuple const & word)
{
string const word_str = to_utf8(word.word());
ignoreAppleSpeller(d->speller, word_str.c_str(), word.lang()->code().c_str());
}
void AppleSpellChecker::suggest(WordLangTuple const & wl,
docstring_list & suggestions)
{
suggestions.clear();
string const word_str = to_utf8(wl.word());
size_t num = makeSuggestionAppleSpeller(d->speller, word_str.c_str(), wl.lang()->code().c_str());
for (size_t i = 0; i < num; i++) {
char const * next = getSuggestionAppleSpeller(d->speller, i);
if (!next) break;
suggestions.push_back(from_utf8(next));
}
}
bool AppleSpellChecker::hasDictionary(Language const * lang) const
{
return hasLanguageAppleSpeller(d->speller,lang->code().c_str());
}
docstring const AppleSpellChecker::error()
{
return docstring();
}
} // namespace lyx

43
src/AppleSpellChecker.h Normal file
View File

@ -0,0 +1,43 @@
// -*- C++ -*-
/**
* \file AppleSpellChecker.h
* This file is part of LyX, the document processor.
* Licence details can be found in the file COPYING.
*
* \author Stephan Witt
*
* Full author contact details are available in file CREDITS.
*/
#ifndef LYX_APPLESPELL_H
#define LYX_APPLESPELL_H
#include "SpellChecker.h"
namespace lyx {
class AppleSpellChecker : public SpellChecker
{
public:
AppleSpellChecker();
~AppleSpellChecker();
/// \name SpellChecker inherited methods
//@{
enum Result check(WordLangTuple const &);
void suggest(WordLangTuple const &, docstring_list &);
void insert(WordLangTuple const &);
void accept(WordLangTuple const &);
bool hasDictionary(Language const * lang) const;
docstring const error();
//@}
private:
struct Private;
Private * d;
};
} // namespace lyx
#endif // LYX_APPLESPELL_H

View File

@ -17,6 +17,7 @@
#include "LyX.h"
#include "AppleSpellChecker.h"
#include "AspellChecker.h"
#include "Buffer.h"
#include "BufferList.h"
@ -130,7 +131,7 @@ void reconfigureUserLyXDir()
/// The main application class private implementation.
struct LyX::Impl
{
Impl() : spell_checker_(0), aspell_checker_(0), enchant_checker_(0), hunspell_checker_(0)
Impl() : spell_checker_(0), apple_spell_checker_(0), aspell_checker_(0), enchant_checker_(0), hunspell_checker_(0)
{
// Set the default User Interface language as soon as possible.
// The language used will be derived from the environment
@ -140,6 +141,7 @@ struct LyX::Impl
~Impl()
{
delete apple_spell_checker_;
delete aspell_checker_;
delete enchant_checker_;
delete hunspell_checker_;
@ -187,6 +189,8 @@ struct LyX::Impl
///
SpellChecker * spell_checker_;
///
SpellChecker * apple_spell_checker_;
///
SpellChecker * aspell_checker_;
///
SpellChecker * enchant_checker_;
@ -1324,6 +1328,14 @@ SpellChecker * theSpellChecker()
void setSpellChecker()
{
#ifdef USE_MACOSX_PACKAGING || defined(LYX_PLATFORM_DARWIN10)
if (lyxrc.spellchecker == "native") {
if (!singleton_->pimpl_->apple_spell_checker_)
singleton_->pimpl_->apple_spell_checker_ = new AppleSpellChecker();
singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->apple_spell_checker_;
return;
}
#endif
#if defined(USE_ASPELL)
if (lyxrc.spellchecker == "aspell") {
if (!singleton_->pimpl_->aspell_checker_)

View File

@ -285,7 +285,10 @@ void LyXRC::setDefaults()
backupdir_path.erase();
display_graphics = true;
// Spellchecker settings:
#if defined(USE_ASPELL)
// FIXME: this check should test the target platform (darwin)
#ifdef USE_MACOSX_PACKAGING || defined(LYX_PLATFORM_DARWIN10)
spellchecker = "native";
#elif defined(USE_ASPELL)
spellchecker = "aspell";
#elif defined(USE_HUNSPELL)
spellchecker = "hunspell";

View File

@ -0,0 +1,35 @@
// -*- C++ -*-
/**
* \file AppleSpeller.h
* This file is part of LyX, the document processor.
* Licence details can be found in the file COPYING.
*
* \author Stephan Witt
*
* Full author contact details are available in file CREDITS.
*/
#ifndef LYX_SUPPORT_SPELLCHECK_H
#define LYX_SUPPORT_SPELLCHECK_H
#ifdef __cplusplus
extern "C" {
#endif
typedef struct AppleSpellerRec * AppleSpeller ;
AppleSpeller newAppleSpeller(void);
void freeAppleSpeller(AppleSpeller speller);
int checkAppleSpeller(AppleSpeller speller, const char * word, const char * lang);
void ignoreAppleSpeller(AppleSpeller speller, const char * word, const char * lang);
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, const char * lang);
int hasLanguageAppleSpeller(AppleSpeller speller, const char * lang);
#ifdef __cplusplus
} // extern "C"
#endif
#endif

197
src/support/AppleSpeller.m Normal file
View File

@ -0,0 +1,197 @@
/**
* \file AppleSpeller.m
* This file is part of LyX, the document processor.
* Licence details can be found in the file COPYING.
*
* \author Stephan Witt
*
* Full author contact details are available in file CREDITS.
*/
#include <Carbon/Carbon.h>
#include <Cocoa/Cocoa.h>
#include "support/AppleSpeller.h"
typedef struct AppleSpellerRec {
NSSpellChecker * checker;
#if defined(LYX_PLATFORM_DARWIN10) && (LYX_PLATFORM_DARWIN10 >= 5)
NSInteger doctag;
#else
int doctag;
#endif
char ** suggestions;
size_t numsug;
} AppleSpellerRec ;
static void freeSuggestionsAppleSpeller(AppleSpeller speller)
{
if (speller->suggestions) {
while (speller->numsug--) {
free(speller->suggestions[speller->numsug]);
}
free(speller->suggestions);
speller->suggestions = 0;
}
}
AppleSpeller newAppleSpeller(void)
{
AppleSpeller speller = calloc(1, sizeof(AppleSpellerRec));
speller->checker = [NSSpellChecker sharedSpellChecker];
speller->suggestions = 0;
speller->doctag = [NSSpellChecker uniqueSpellDocumentTag];
return speller;
}
void freeAppleSpeller(AppleSpeller speller)
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
freeSuggestionsAppleSpeller(speller);
[speller->checker closeSpellDocumentWithTag:speller->doctag];
[pool release];
free(speller);
}
static NSString * toString(AppleSpeller speller, const char * word)
{
return [[NSString alloc] initWithBytes:word length:strlen(word) encoding:NSUTF8StringEncoding];
}
int checkAppleSpeller(AppleSpeller speller, const char * word, const char * lang)
{
if (!speller->checker || !lang || !word)
return 0;
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
#if defined(LYX_PLATFORM_DARWIN10) && (LYX_PLATFORM_DARWIN10 >= 5)
NSInteger wordcount;
#else
int wordcount;
#endif
NSString * word_ = toString(speller, word);
NSString * lang_ = toString(speller, lang);
NSRange result = [speller->checker
checkSpellingOfString:word_
startingAt:0
language:lang_
wrap:(BOOL)NO
inSpellDocumentWithTag:speller->doctag
wordCount:&wordcount];
[word_ release];
[lang_ release];
[pool release];
return (result.length ? 0 : 1);
}
void ignoreAppleSpeller(AppleSpeller speller, const char * word, const char * lang)
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString * word_ = toString(speller, word);
[speller->checker ignoreWord:word_ inSpellDocumentWithTag:(speller->doctag)];
[word_ release];
[pool release];
}
size_t makeSuggestionAppleSpeller(AppleSpeller speller, const char * word, const char * lang)
{
if (!speller->checker || !word || !lang)
return 0;
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString * word_ = toString(speller, word);
NSString * lang_ = toString(speller, lang);
#if defined(LYX_PLATFORM_DARWIN10) && (LYX_PLATFORM_DARWIN10 >= 6)
// Mac OS X 10.6 only
NSInteger slen = [word_ length];
NSRange range = { 0, slen };
NSArray * result = [speller->checker guessesForWordRange:range
inString:word_
language:lang_
inSpellDocumentWithTag:speller->doctag];
#else
[speller->checker setLanguage:lang_];
NSArray * result = [speller->checker guessesForWord:word_];
#endif
[word_ release];
[lang_ release];
freeSuggestionsAppleSpeller(speller);
speller->numsug = [result count];
if (speller->numsug) {
speller->suggestions = calloc(speller->numsug + 1, sizeof(char *));
if (speller->suggestions) {
size_t i;
for (i = 0; i < speller->numsug; i++) {
NSString * str = [result objectAtIndex:i];
speller->suggestions[i] = strdup([str UTF8String]);
}
speller->suggestions[speller->numsug] = 0;
}
}
[pool release];
return speller->numsug;
}
const char * getSuggestionAppleSpeller(AppleSpeller speller, size_t pos)
{
const char * result = 0;
if (pos < speller->numsug && speller->suggestions) {
result = speller->suggestions[pos] ;
}
return result;
}
void learnAppleSpeller(AppleSpeller speller, const char * word, const char * lang)
{
#if defined(LYX_PLATFORM_DARWIN10) && (LYX_PLATFORM_DARWIN10 >= 5)
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString * word_ = toString(speller, word);
[speller->checker learnWord:word_];
[word_ release];
[pool release];
#endif
}
int hasLanguageAppleSpeller(AppleSpeller speller, const char * lang)
{
BOOL result = NO;
#if defined(LYX_PLATFORM_DARWIN10) && (LYX_PLATFORM_DARWIN10 >= 5)
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString * lang_ = toString(speller, lang);
NSArray * languages = [speller->checker availableLanguages];
for (NSString *element in languages) {
result = [element isEqualToString:lang_] || [lang_ hasPrefix:element];
if (result) break;
}
[lang_ release];
[pool release];
#endif
return result ? 1 : 0;
}

View File

@ -103,8 +103,16 @@ liblyxsupport_a_SOURCES = \
mythes/mythes.hxx \
mythes/license.readme
#if INSTALL_MACOSX
#liblyxsupport_a_SOURCES += \
# AppleSpellChecker.h \
# AppleSpellChecker.mm
#endif
if INSTALL_MACOSX
liblyxsupport_a_SOURCES += \
AppleSpeller.h \
AppleSpeller.m \
linkback/LinkBack.h \
linkback/LinkBack.m \
linkback/LinkBackProxy.h \