Dear LyX developers, I've prepared a patch to change hunspell and myThes wrappers to be ready for included dictionaries. Please have a look at the patch and point me to typos and thinkos. With the changes the binary searches the data files in this order: 1. user path from configuration 2. user support directory and 3. system support directory - there will the shipped data files live.
The next steps on my agenda are * apple spell checker * aspell fix (if possible) * dictionary collection Regards, Stephan
Index: development/LyX-Mac-binary-release.sh =================================================================== --- development/LyX-Mac-binary-release.sh (Revision 34532) +++ development/LyX-Mac-binary-release.sh (Arbeitskopie) @@ -9,12 +9,27 @@ # Prerequisite: # * a decent checkout of LyX sources (probably you have it already) -# * Qt4 - build with shared or static libraries for the used platforms (i386 and ppc) +# * Qt4 - build with shared or static libraries for the used platforms (default: i386 and ppc) +# or - an unpacked source tree of Qt4 in $QT4SOURCEDIR or in the sibling directory (variable Qt4SourceVersion) # * for aspell support: # the aspell sources placed in a sibling directory (variable ASpellSourceVersion) +# * for hunspell support: +# the hunspell sources placed in a sibling directory (variable HunSpellSourceVersion) +# * for dictionary deployment: +# - aspell: the dictionary files of macports (in /opt/local/share/aspell and /opt/local/lib/aspell-0.60) +# - hunspell: the dictionary files in the sibling directory Dictionaries/dict +# - mythes: the data and idx files in the sibling directory Dictionaries/thes -ConfigureOptions="--enable-warnings --enable-optimization=-Os --with-included-gettext" -dict_deployment="yes" +LyXConfigureOptions="--enable-warnings --enable-optimization=-Os --with-included-gettext" +AspellConfigureOptions="--enable-warnings --disable-nls --enable-compile-in-filters --disable-pspell-compatibility" +HunspellConfigureOptions="--with-warnings --disable-nls --with-included-gettext --disable-static" +Qt4ConfigureOptions="-opensource -silent -shared -release -fast -no-exceptions" +Qt4ConfigureOptions="${Qt4ConfigureOptions} -no-webkit -no-qt3support -no-javascript-jit -no-dbus" +Qt4ConfigureOptions="${Qt4ConfigureOptions} -nomake examples -nomake demos -nomake docs -nomake tools" + +aspell_deployment="yes" +hunspell_deployment="yes" +thesaurus_deployment="yes" qt4_deployment="yes" MACOSX_DEPLOYMENT_TARGET="10.4" # Tiger support is default @@ -22,15 +37,15 @@ echo Build script for LyX on Mac OS X echo echo Optional arguments: - echo " --tiger-support=yes|no ....." default yes - echo " --dict-deployment=yes|no ..." default yes + echo " --aspell-deployment=yes|no ." default yes echo " --qt4-deployment=yes|no ...." default yes + echo " --with-macosx-target=TARGET " default 10.4 "(Tiger)" echo " --with-arch=ARCH ..........." default ppc,i386 echo " --with-build-path=PATH ....." default \${lyx-src-dir}/../lyx-build echo " --with-dmg-location=PATH ..." default \${build-path} echo echo "All other arguments with -- are passed to configure" - echo "including the defaults: ${ConfigureOptions}" + echo "including the defaults: ${LyXConfigureOptions}" echo exit 0 } @@ -41,15 +56,22 @@ QTDIR=`echo ${1}|cut -d= -f2` shift ;; - --tiger-support=[Nn][Oo]) - MACOSX_DEPLOYMENT_TARGET="" - MYCFLAGS="" + --with-macosx-target=*) + MACOSX_DEPLOYMENT_TARGET=`echo ${1}|cut -d= -f2` shift ;; - --dict-deployment=*) - dict_deployment=`echo ${1}|cut -d= -f2` + --aspell-deployment=*) + aspell_deployment=`echo ${1}|cut -d= -f2` shift ;; + --hunspell-deployment=*) + hunspell_deployment=`echo ${1}|cut -d= -f2` + shift + ;; + --thesaurus-deployment=*) + thesaurus_deployment=`echo ${1}|cut -d= -f2` + shift + ;; --qt4-deployment=*) qt4_deployment=`echo ${1}|cut -d= -f2` shift @@ -70,8 +92,18 @@ --help) usage ;; + --without-aspell) + LyXConfigureOptions="${LyXConfigureOptions} ${1}" + aspell_deployment="no" + shift + ;; + --without-hunspell) + LyXConfigureOptions="${LyXConfigureOptions} ${1}" + hunspell_deployment="no" + shift + ;; --*) - ConfigureOptions="${ConfigureOptions} ${1}" + LyXConfigureOptions="${LyXConfigureOptions} ${1}" shift ;; *) @@ -90,6 +122,9 @@ QtInstallDir=${QTDIR:-"/opt/qt4"} QtFrameworkVersion="4" ASpellSourceVersion="aspell-0.60.6" +HunSpellSourceVersion="hunspell-1.2.9" +Qt4SourceVersion="qt-everywhere-opensource-src-4.6.2" + ARCH_LIST=${ARCH_LIST:-"ppc i386"} strip="-strip" @@ -119,11 +154,16 @@ DMGLocation=${DMGLocation:-"${LyxBuildDir}"} ASpellSourceDir=${ASPELLDIR:-`dirname "${LyxSourceDir}"`/${ASpellSourceVersion}} -ASpellInstallDir=${ASpellInstallDir:-"${LyxBuildDir}"/${ASpellSourceVersion}.lib} +ASpellInstallDir=${ASpellInstallDir:-"${LyxBuildDir}"/SpellChecker.lib} +HunSpellSourceDir=${HUNSPELLDIR:-`dirname "${LyxSourceDir}"`/${HunSpellSourceVersion}} +HunSpellInstallDir=${HunSpellInstallDir:-"${LyxBuildDir}"/SpellChecker.lib} +Qt4SourceDir=${QT4SOURCEDIR:-`dirname "${LyxSourceDir}"`/${Qt4SourceVersion}} +Qt4BuildDir="${LyxBuildDir}/qt4-build" +DictionarySourceDir=${DICTIONARYDIR:-`dirname "${LyxSourceDir}"`/Dictionaries} -if [ ! -f "${LyxSourceDir}"/configure ]; then - ( cd "${LyxSourceDir}" && sh autogen.sh ) -fi +ASpellInstallHdr="${ASpellInstallDir}/include/aspell.h" +HunSpellInstallHdr="${HunSpellInstallDir}/include/hunspell/hunspell.h" + if [ -z "${LyXVersion}" ]; then LyXVersion=`grep AC_INIT "${LyxSourceDir}"/configure.ac | cut -d, -f2 | tr -d " ()"` fi @@ -151,9 +191,11 @@ HostSystem_i386="i686-apple-darwin8" HostSystem_ppc="powerpc-apple-darwin8" -DMGNAME="${LyxBase}-Uncompressed.dmg" +# don't change order here... +QtLibraries="QtSvg QtXml QtGui QtNetwork QtCore" + +DMGNAME="${LyxBase}" DMGSIZE="550m" -COMPRESSEDDMGNAME="${LyxBase}.dmg" BACKGROUND="${LyxAppDir}.app/Contents/Resources/images/banner.png" # Check for existing SDKs @@ -184,7 +226,97 @@ esac MYCFLAGS="-mmacosx-version-min=${MACOSX_DEPLOYMENT_TARGET}" -if [ -d "${ASpellSourceDir}" -a ! -d "${ASpellInstallDir}" ]; then +if [ -d "${Qt4SourceDir}" -a ! -d "${Qt4BuildDir}" ]; then + echo Build Qt4 library ${Qt4SourceDir} + + ( + mkdir -p "${Qt4BuildDir}" && cd "${Qt4BuildDir}" + for arch in ${ARCH_LIST} ; do + ARCHS="${ARCHS} -arch ${arch}" + done + echo configure options: + echo ${Qt4ConfigureOptions} ${ARCHS} -prefix "${QtInstallDir}" + + echo yes | "${Qt4SourceDir}"/configure ${Qt4ConfigureOptions} ${ARCHS} -prefix "${QtInstallDir}" + make && make install + ) + cd "${QtInstallDir}" && ( + mkdir -p include + cd include + for libnm in ${QtLibraries} ; do + test -d ${libnm} -o -L ${libnm} || ln -s ../lib/${libnm}.framework/Headers ${libnm} + done + ) +fi + +if [ -d "${HunSpellSourceDir}" -a ! -f "${HunSpellInstallHdr}" ]; then + # we have a private HunSpell source tree at hand... + # so let's build and install it + if [ -z "${HunSpellVersion}" ]; then + HunSpellVersion=`grep AC_INIT "${HunSpellSourceDir}"/configure.ac | cut -d, -f2|tr -d " ()"` + fi + + HunSpellName="Hunspell" + HunSpellBase="${HunSpellName}-${HunSpellVersion}" + + echo Build hunspell library ${HunSpellBase} + echo configure options: + echo --prefix="${HunSpellInstallDir}" ${HunspellConfigureOptions} + + cd "${HunSpellSourceDir}" + + # ---------------------------------------- + # Build HunSpell for different architectures + # ---------------------------------------- + FILE_LIST="libhunspell-1.2.0.dylib" + + for arch in ${ARCH_LIST} ; do + make distclean + CPPFLAGS=" -arch ${arch} ${MYCFLAGS}"; export CPPFLAGS + LDFLAGS=" -arch ${arch}"; export LDFLAGS + HOSTSYSTEM=`eval "echo \\$HostSystem_$arch"` + "${HunSpellSourceDir}/configure"\ + --prefix="${HunSpellInstallDir}"\ + ${HunspellConfigureOptions} + #--host="${HOSTSYSTEM}" ${BuildSystem:+"--build=${BuildSystem}"} + make && make install${strip} + for file in ${FILE_LIST} ; do + if [ -f "${HunSpellInstallDir}"/lib/${file} ]; then + mv "${HunSpellInstallDir}"/lib/${file}\ + "${HunSpellInstallDir}"/lib/${file}-${arch} + else + echo Cannot build and install HunSpell for ${arch}. + exit 1 + fi + done + done + # ------------------------- + # Create universal binaries + # ------------------------- + for file in ${FILE_LIST} ; do + OBJ_LIST= + for arch in ${ARCH_LIST} ; do + OBJ_LIST="${OBJ_LIST} lib/${file}-${arch}" + done + ( + cd "${HunSpellInstallDir}" + lipo -create ${OBJ_LIST} -o lib/${file} + # check for the "missing link"... + test -f lib/libhunspell.dylib || (cd lib ; ln -s libhunspell-1.2.dylib libhunspell.dylib) + ) + done + # -------- + # Clean up + # -------- + for arch in ${ARCH_LIST} ; do + rm -f "${HunSpellInstallDir}"/lib/*-${arch} + 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 if [ -z "${ASpellVersion}" ]; then @@ -195,27 +327,26 @@ ASpellBase="${ASpellName}-${ASpellVersion}" echo Build aspell library ${ASpellBase} + echo configure options: + echo --prefix="${ASpellInstallDir}" ${AspellConfigureOptions} - # Clear Output - if [ -n "${ASpellLibZip}" -a -f "${ASpellLibZip}" ]; then rm "${ASpellLibZip}"; fi - if [ -d "${ASpellInstallDir}" ]; then rm -r "${ASpellInstallDir}"; fi - # ASpell builds inplace only :( - cd "${ASpellSourceDir}" && make distclean + cd "${ASpellSourceDir}" # ---------------------------------------- # Build ASpell for different architectures # ---------------------------------------- - FILE_LIST="libaspell.15.dylib libpspell.15.dylib" + FILE_LIST="libaspell.15.dylib" for arch in ${ARCH_LIST} ; do - CPPFLAGS="${SDKROOT:+-isysroot ${SDKROOT}} -arch ${arch} ${MYCFLAGS}"; export CPPFLAGS - LDFLAGS="${SDKROOT:+-isysroot ${SDKROOT}} -arch ${arch}"; export LDFLAGS + make distclean + CPPFLAGS=" -arch ${arch} ${MYCFLAGS}"; export CPPFLAGS + LDFLAGS=" -arch ${arch}"; export LDFLAGS HOSTSYSTEM=`eval "echo \\$HostSystem_$arch"` "${ASpellSourceDir}/configure"\ --prefix="${ASpellInstallDir}"\ - ${ConfigureOptions}\ - --host="${HOSTSYSTEM}" ${BuildSystem:+"--build=${BuildSystem}"} --enable-build-type=rel + ${AspellConfigureOptions} + #--host="${HOSTSYSTEM}" ${BuildSystem:+"--build=${BuildSystem}"} make && make install${strip} for file in ${FILE_LIST} ; do if [ -f "${ASpellInstallDir}"/lib/${file} ]; then @@ -226,7 +357,6 @@ exit 1 fi done - make distclean done # ------------------------- # Create universal binaries @@ -253,6 +383,10 @@ echo "Frameworks/${1}.framework" } +if [ ! -f "${LyxSourceDir}"/configure ]; then + ( cd "${LyxSourceDir}" && sh autogen.sh ) +fi + FILE_LIST="lyx lyxclient tex2lyx" BUNDLE_PATH="Contents/MacOS" LYX_BUNDLE_PATH="${LyxAppPrefix}/${BUNDLE_PATH}" @@ -265,14 +399,24 @@ # Build LyX for different architectures # ------------------------------------- - if [ -d "${ASpellInstallDir}" ]; then + if [ -d "${ASpellInstallDir}" -a "yes" = "${aspell_deployment}" ]; then ASpellFramework=`framework_name Aspell` ASpellFramework=`basename "${ASpellFramework}"` - ConfigureOptions="${ConfigureOptions} --with-extra-inc=${ASpellInstallDir}/include" - ConfigureOptions="${ConfigureOptions} --with-extra-lib=${ASpellInstallDir}/lib" - ConfigureOptions="${ConfigureOptions} --with-aspell-framework=${ASpellFramework}" + ConfigureExtraInc="--with-extra-inc=${ASpellInstallDir}/include" + ConfigureExtraLib="--with-extra-lib=${ASpellInstallDir}/lib" + LyXConfigureOptions="${LyXConfigureOptions} --with-aspell-framework=${ASpellFramework}" fi + if [ -d "${HunSpellInstallDir}" -a "yes" = "${hunspell_deployment}" ]; then + HunSpellFramework=`framework_name Hunspell` + HunSpellFramework=`basename "${HunSpellFramework}"` + ConfigureExtraInc="--with-extra-inc=${HunSpellInstallDir}/include" + ConfigureExtraLib="--with-extra-lib=${HunSpellInstallDir}/lib" + # LyXConfigureOptions="${LyXConfigureOptions} --with-hunspell-framework=${HunSpellFramework}" + fi + LyXConfigureOptions="${LyXConfigureOptions} ${ConfigureExtraInc}" + LyXConfigureOptions="${LyXConfigureOptions} ${ConfigureExtraLib}" + for arch in ${ARCH_LIST} ; do if [ -d "${LyxBuildDir}" ]; then rm -r "${LyxBuildDir}"; fi @@ -284,11 +428,11 @@ echo LDFLAGS="${LDFLAGS}" echo CPPFLAGS="${CPPFLAGS}" - echo CONFIGURE_OPTIONS="${ConfigureOptions}" + echo CONFIGURE_OPTIONS="${LyXConfigureOptions}" "${LyxSourceDir}/configure"\ --prefix="${LyxAppPrefix}" --with-version-suffix="-${LyXVersion}"\ ${QtInstallDir:+"--with-qt4-dir=${QtInstallDir}"} \ - ${ConfigureOptions}\ + ${LyXConfigureOptions}\ --host="${HOSTSYSTEM}" --build="${BuildSystem}" --enable-build-type=rel make && make install${strip} for file in ${FILE_LIST} ; do @@ -347,8 +491,7 @@ cp -p "${libname}" "${condir}/PlugIns/${dirname}" done fi - # don't change order here... - for libnm in QtSvg QtXml QtGui QtNetwork QtCore ; do + for libnm in ${QtLibraries} ; do fwdir=`framework_name "$libnm"` dirname=`basename "${fwdir}"` test -d "${condir}/${fwdir}" || ( @@ -394,9 +537,12 @@ if [ -n "${OBJ_LIST}" ]; then lipo -create ${OBJ_LIST} -o "${BUNDLE_PATH}/${file}" fi - if [ -d "${ASpellInstallDir}" ]; then + if [ -d "${ASpellInstallDir}" -a "yes" = "${aspell_deployment}" ]; then private_framework Aspell "${ASpellInstallDir}/lib/libaspell.15.dylib" "${LYX_BUNDLE_PATH}/${file}" fi + if [ -d "${HunSpellInstallDir}" -a "yes" = "${hunspell_deployment}" ]; then + private_framework Hunspell "${HunSpellInstallDir}/lib/libhunspell-1.2.0.dylib" "${LYX_BUNDLE_PATH}/${file}" + fi if [ -d "${QtInstallDir}/lib/QtCore.framework/Versions/${QtFrameworkVersion}" -a "yes" = "${qt4_deployment}" ]; then deploy_qtlibs "${LYX_BUNDLE_PATH}/${file}" fi @@ -414,7 +560,7 @@ } copy_dictionaries() { - if [ -d "${ASpellInstallDir}" -a "yes" = "${dict_deployment}" ]; then + if [ -d "${ASpellInstallDir}" -a "yes" = "${aspell_deployment}" ]; then ASpellFramework=`framework_name Aspell` ASpellResources="${LyxAppPrefix}/Contents/${ASpellFramework}/Resources" # try to reuse macports dictionaries for now @@ -424,6 +570,16 @@ 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 + cp -p -r "${DictionarySourceDir}/dict" "${HunSpellResources}" + fi + fi + if [ -d "${DictionarySourceDir}" -a "yes" = "${thesaurus_deployment}" ]; then + MyThesResources="${LyxAppPrefix}/Contents/Resources" + cp -p -r "${DictionarySourceDir}/thes" "${MyThesResources}" + fi } function set_bundle_display_options() { @@ -464,38 +620,37 @@ BG_H=`echo ${BGSIZE} | awk '{h = $2 + 20 ;print h }'` BG_Y=`echo ${BGSIZE} | awk '{y = $2 - 60 ;print y }'` - rm -f ${DMGNAME} - rm -f ${COMPRESSEDDMGNAME} + rm -f "${DMGNAME}.sparseimage" "${DMGNAME}.dmg" hdiutil create -type SPARSE -size ${DMGSIZE:-"250m"} -fs HFS+ -volname "${LyxBase}" "${DMGNAME}" # Unmount currently mounted disk image test -d /Volumes/"${LyxBase}" && umount /Volumes/"${LyxBase}" # Mount the disk image - hdiutil attach ${DMGNAME}.sparseimage + hdiutil attach "${DMGNAME}.sparseimage" # Obtain device information - DEVS=$(hdiutil attach ${DMGNAME}.sparseimage | cut -f 1) + DEVS=$(hdiutil attach "${DMGNAME}.sparseimage" | cut -f 1) DEV=$(echo $DEVS | cut -f 1 -d ' ') VOLUME=$(mount |grep ${DEV} | cut -f 3 -d ' ') # copy in the application bundle - cp -Rp ${LyxAppDir}.app ${VOLUME}/${LyxName}.app + cp -Rp "${LyxAppDir}.app" "${VOLUME}/${LyxName}.app" # copy in background image - mkdir -p ${VOLUME}/Pictures - cp ${BACKGROUND} ${VOLUME}/Pictures/background.png + mkdir -p "${VOLUME}/Pictures" + cp "${BACKGROUND}" "${VOLUME}/Pictures/background.png" # symlink applications - ln -s /Applications/ ${VOLUME}/Applications - set_bundle_display_options ${VOLUME} ${BG_W} ${BG_H} ${BG_Y} - mv ${VOLUME}/Pictures ${VOLUME}/.Pictures + ln -s /Applications/ "${VOLUME}"/Applications + set_bundle_display_options "${VOLUME}" ${BG_W} ${BG_H} ${BG_Y} + mv "${VOLUME}/Pictures" "${VOLUME}/.Pictures" # Unmount the disk image hdiutil detach ${DEV} # Convert the disk image to read-only - hdiutil convert ${DMGNAME}.sparseimage -format UDBZ -o ${COMPRESSEDDMGNAME} - rm -f ${DMGNAME}.sparseimage + hdiutil convert "${DMGNAME}.sparseimage" -format UDBZ -o "${DMGNAME}.dmg" + rm -f "${DMGNAME}.sparseimage" } build_lyx @@ -510,4 +665,15 @@ cd "${LyxAppPrefix}" && zip -r "${LyxAppZip}" . ) -test -n "${DMGLocation}" && make_dmg "${DMGLocation}" +test -n "${DMGLocation}" && ( + make_dmg "${DMGLocation}" + if [ -d "${QtInstallDir}/lib/QtCore.framework/Versions/${QtFrameworkVersion}" -a "yes" = "${qt4_deployment}" ]; then + rm -f "${DMGLocation}/${DMGNAME}+qt4.dmg" + mv "${DMGLocation}/${DMGNAME}.dmg" "${DMGLocation}/${DMGNAME}+qt4.dmg" + for libnm in ${QtLibraries} ; do + fwdir=`framework_name "$libnm"` + rm -rf "${LyxAppDir}.app/Contents/${fwdir}" + done + make_dmg "${DMGLocation}" + fi +) Index: src/HunspellChecker.cpp =================================================================== --- src/HunspellChecker.cpp (Revision 34532) +++ src/HunspellChecker.cpp (Arbeitskopie) @@ -20,6 +20,7 @@ #include "support/debug.h" #include "support/docstring_list.h" #include "support/filetools.h" +#include "support/Package.h" #include "support/FileName.h" #include "support/gettext.h" #include "support/lassert.h" @@ -45,6 +46,10 @@ } // anon namespace +#ifndef HUNSPELL_DICT +# define HUNSPELL_DICT "dict" +#endif + struct HunspellChecker::Private { Private() {} @@ -52,6 +57,8 @@ ~Private(); bool haveDictionary(string const & lang, string & hpath); + bool haveDictionary(string const & lang); + Hunspell * addSpeller(string const & lang, string & hpath); Hunspell * addSpeller(string const & lang); Hunspell * speller(string const & lang); /// ignored words @@ -70,7 +77,7 @@ Spellers::iterator end = spellers_.end(); for (; it != end; ++it) { - delete it->second; + if ( 0 != it->second) delete it->second; } } @@ -80,67 +87,54 @@ { FileName const affix(hpath + ".aff"); FileName const dict(hpath + ".dic"); - if (!affix.isReadableFile()) { - // FIXME: We should indicate somehow that this language is not - // supported. - LYXERR(Debug::FILES, "Hunspell affix file " << affix << " does not exist"); - return false; + return affix.isReadableFile() && dict.isReadableFile(); +} + + +#define MAX_SELECTOR 3 +string dictPath(int selector) +{ + switch (selector) { + case 2: return addName(lyx::support::package().system_support().absFileName(),HUNSPELL_DICT) ; break ; + case 1: return addName(lyx::support::package().user_support().absFileName(),HUNSPELL_DICT) ; break ; + default: return lyxrc.hunspelldir_path ; } - if (!dict.isReadableFile()) { - LYXERR(Debug::FILES, "Hunspell dictionary file " << dict << " does not exist"); - return false; - } - return true; } + } -bool HunspellChecker::Private::haveDictionary(string const & lang, string & hunspell_path) +bool HunspellChecker::Private::haveDictionary(string const & lang, string & hpath) { - LYXERR(Debug::FILES, "hunspell path: " << external_path(hunspell_path)); - if (hunspell_path.empty()) { - // FIXME We'd like to issue a better error message here, but there seems - // to be a problem about thread safety, or something of the sort. If - // we issue the message using frontend::Alert, then the code comes - // back through here while the box is waiting, and causes some kind - // of crash. - static bool warned = false; - if (!warned) { - warned = true; - LYXERR0("Hunspell path not set."); - //frontend::Alert::error(_("Hunspell Path Not Found"), - // _("You must set the Hunspell dictionary path in Tools>Preferences>Paths.")); - } + if (hpath.empty()) { return false; } - hunspell_path = external_path(addName(hunspell_path, lang)); - if (!haveLanguageFiles(hunspell_path)) { + LYXERR(Debug::FILES, "check hunspell path: " << hpath << " for language " << lang); + string h_path = addName(hpath, lang); + if (!haveLanguageFiles(h_path)) { // try with '_' replaced by '-' - hunspell_path = subst(hunspell_path, '_', '-'); - if (!haveLanguageFiles(hunspell_path)) { + h_path = addName(hpath, subst(lang, '_', '-')); + if (!haveLanguageFiles(h_path)) { // FIXME: We should indicate somehow that this language is not // supported, probably by popping a warning. But we'll need to // remember which warnings we've issued. return false; } } + hpath = h_path; return true; } -Hunspell * HunspellChecker::Private::addSpeller(string const & lang) +bool HunspellChecker::Private::haveDictionary(string const & lang) { - string hunspell_path = lyxrc.hunspelldir_path; - - if (!haveDictionary(lang, hunspell_path)) - return 0; - - FileName const affix(hunspell_path + ".aff"); - FileName const dict(hunspell_path + ".dic"); - Hunspell * h = new Hunspell(affix.absFileName().c_str(), dict.absFileName().c_str()); - spellers_[lang] = h; - return h; + bool result = false; + for ( int p = 0 ; !result && p < MAX_SELECTOR ; p++ ) { + string lpath = dictPath(p); + result = haveDictionary(lang, lpath); + } + return result ; } @@ -154,6 +148,33 @@ } +Hunspell * HunspellChecker::Private::addSpeller(string const & lang,string & path) +{ + if (!haveDictionary(lang, path)) { + spellers_[lang] = 0; + return 0; + } + + FileName const affix(path + ".aff"); + FileName const dict(path + ".dic"); + Hunspell * h = new Hunspell(affix.absFileName().c_str(), dict.absFileName().c_str()); + LYXERR(Debug::FILES, "Hunspell speller for langage " << lang << " at " << dict << " found"); + spellers_[lang] = h; + return h; +} + + +Hunspell * HunspellChecker::Private::addSpeller(string const & lang) +{ + Hunspell * h = 0 ; + for ( int p = 0 ; p < MAX_SELECTOR && 0 == h ; p++ ) { + string lpath = dictPath(p); + h = addSpeller(lang, lpath); + } + return h; +} + + bool HunspellChecker::Private::isIgnored(WordLangTuple const & wl) const { IgnoreList::const_iterator it = ignored_.begin(); @@ -246,8 +267,7 @@ { if (!lang) return false; - string hunspell_path = lyxrc.hunspelldir_path; - return (d->haveDictionary(lang->code(), hunspell_path)); + return (d->haveDictionary(lang->code())); } Index: src/Thesaurus.cpp =================================================================== --- src/Thesaurus.cpp (Revision 34532) +++ src/Thesaurus.cpp (Arbeitskopie) @@ -16,6 +16,7 @@ #include "LyXRC.h" #include "support/FileNameList.h" +#include "support/Package.h" #include "support/debug.h" #include "support/filetools.h" #include "support/gettext.h" @@ -41,6 +42,9 @@ } // namespace anon +#ifndef THESAURUS_LOCATION +# define THESAURUS_LOCATION "thes" +#endif struct Thesaurus::Private { @@ -66,6 +70,7 @@ /// typedef std::pair<std::string, std::string> ThesFiles; /// + ThesFiles getThesaurus(string const & path, docstring const & lang); ThesFiles getThesaurus(docstring const & lang); /// add a thesaurus to the list bool addThesaurus(docstring const & lang); @@ -75,45 +80,62 @@ }; -pair<string, string> Thesaurus::Private::getThesaurus(docstring const & lang) +pair<string,string> Thesaurus::Private::getThesaurus(string const & path, docstring const & lang) { - string const thes_path = external_path(lyxrc.thesaurusdir_path); - LYXERR(Debug::FILES, "thesaurus path: " << thes_path); - if (thes_path.empty()) + FileName base(path); + if (!base.isDirectory()) { return make_pair(string(), string()); - - if (thesaurusAvailable(lang)) - return make_pair(string(), string()); - - FileNameList const idx_files = FileName(thes_path).dirList("idx"); - FileNameList const data_files = FileName(thes_path).dirList("dat"); + } + FileNameList const idx_files = base.dirList("idx"); + FileNameList const data_files = base.dirList("dat"); string idx; string data; - for (FileNameList::const_iterator it = idx_files.begin(); - it != idx_files.end(); ++it) { - LYXERR(Debug::FILES, "found thesaurus idx file: " << it->onlyFileName()); + LYXERR(Debug::FILES, "thesaurus path: " << path); + for (FileNameList::const_iterator it = idx_files.begin(); it != idx_files.end(); ++it) { if (contains(it->onlyFileName(), to_ascii(lang))) { idx = it->absFileName(); LYXERR(Debug::FILES, "selected thesaurus idx file: " << idx); break; - } } - - for (support::FileNameList::const_iterator it = data_files.begin(); - it != data_files.end(); ++it) { - LYXERR(Debug::FILES, "found thesaurus data file: " << it->onlyFileName()); + } + if (idx.empty()) { + return make_pair(string(), string()); + } + for (support::FileNameList::const_iterator it = data_files.begin(); it != data_files.end(); ++it) { if (contains(it->onlyFileName(), to_ascii(lang))) { data = it->absFileName(); LYXERR(Debug::FILES, "selected thesaurus data file: " << data); break; - } } - + } return make_pair(idx, data); } +pair<string,string> Thesaurus::Private::getThesaurus(docstring const & lang) +{ + string const thes_path = external_path(lyxrc.thesaurusdir_path); + pair<string,string> result ; + + if (thesaurusAvailable(lang)) + return make_pair(string(), string()); + + if (!thes_path.empty()) { + result = getThesaurus(thes_path, lang); + } + if (result.first.empty() || result.second.empty()) { + string const sys_path = external_path(addName(lyx::support::package().system_support().absFileName(),THESAURUS_LOCATION)) ; + result = getThesaurus(sys_path, lang); + } + if (result.first.empty() || result.second.empty()) { + string const user_path = external_path(addName(lyx::support::package().user_support().absFileName(),THESAURUS_LOCATION)) ; + result = getThesaurus(user_path, lang); + } + return result; +} + + bool Thesaurus::Private::addThesaurus(docstring const & lang) { if (thesaurusAvailable(lang)) Index: src/AspellChecker.cpp =================================================================== --- src/AspellChecker.cpp (Revision 34532) +++ src/AspellChecker.cpp (Arbeitskopie) @@ -19,6 +19,7 @@ #include "support/debug.h" #include "support/docstring_list.h" +#include "support/Package.h" #include "support/FileName.h" #include "support/Path.h" @@ -27,15 +28,41 @@ #include <map> #include <string> +#ifdef __APPLE__ + +# ifndef ASPELL_FRAMEWORK +# define ASPELL_FRAMEWORK "Aspell.framework" +# endif +# ifndef ASPELL_FRAMEWORK_DATA +# define ASPELL_FRAMEWORK_DATA "/Resources/data" +# endif +# ifndef ASPELL_FRAMEWORK_DICT +# define ASPELL_FRAMEWORK_DICT "/Resources/dict" +# endif + +# ifndef ASPELL_MACPORTS +# define ASPELL_MACPORTS "/opt/local" +# endif +# ifndef ASPELL_MACPORTS_DATA +# define ASPELL_MACPORTS_DATA "/lib/aspell-0.60" +# endif +# ifndef ASPELL_MACPORTS_DICT +# define ASPELL_MACPORTS_DICT "/share/aspell" +# endif + +#endif /* __APPLE__ */ + using namespace std; +using namespace lyx::support; namespace lyx { namespace { struct Speller { - AspellSpeller * speller; + ///AspellSpeller * speller; AspellConfig * config; + AspellCanHaveError * e_speller; }; typedef std::map<std::string, Speller> Spellers; @@ -44,7 +71,7 @@ struct AspellChecker::Private { - Private(): spell_error_object(0) {} + Private() {} ~Private(); @@ -63,61 +90,91 @@ /// the spellers Spellers spellers_; - /// FIXME - AspellCanHaveError * spell_error_object; }; AspellChecker::Private::~Private() { - if (spell_error_object) { - delete_aspell_can_have_error(spell_error_object); - spell_error_object = 0; - } - Spellers::iterator it = spellers_.begin(); Spellers::iterator end = spellers_.end(); for (; it != end; ++it) { - aspell_speller_save_all_word_lists(it->second.speller); - delete_aspell_speller(it->second.speller); + if (it->second.e_speller) { + AspellSpeller * speller = to_aspell_speller(it->second.e_speller); + aspell_speller_save_all_word_lists(speller); + delete_aspell_can_have_error(it->second.e_speller); + } delete_aspell_config(it->second.config); } } -AspellConfig * getConfig() +bool isValidDictionary(AspellConfig * config, + string const & lang, string const & variety) { + bool have = false; + // code taken from aspell's list-dicts example + // the returned pointer should _not_ need to be deleted + AspellDictInfoList * dlist = get_aspell_dict_info_list(config); + AspellDictInfoEnumeration * dels = aspell_dict_info_list_elements(dlist); + const AspellDictInfo * entry; + + while (0 != (entry = aspell_dict_info_enumeration_next(dels))) { + LYXERR(Debug::DEBUG, "aspell dict:" + << " name=" << entry->name + << ",code=" << entry->code + << ",variety=" << entry->jargon); + if (entry->code == lang && (variety.empty() || entry->jargon == variety)) { + have = true; + break; + } + } + delete_aspell_dict_info_enumeration(dels); + LYXERR(Debug::FILES, "aspell dictionary: " << lang << (have ? " yes" : " no")); + return have; +} + + +bool checkAspellData(AspellConfig * config, + char const * basepath, char const * datapath, char const * dictpath, + string const & lang, string const & variety) +{ + bool have_dict = false; + FileName base(basepath); + FileName data(base.absFileName() + datapath); + FileName dict(base.absFileName() + dictpath); + have_dict = dict.isDirectory() && data.isDirectory(); + if (have_dict) { + aspell_config_replace(config, "dict-dir", dict.absFileName().c_str()); + aspell_config_replace(config, "data-dir", data.absFileName().c_str()); + LYXERR(Debug::FILES, "aspell dict: " << dict); + have_dict = isValidDictionary(config, lang, variety); + } + return have_dict ; +} + + +AspellConfig * getConfig(string const & lang, + string const & variety) +{ AspellConfig * config = new_aspell_config(); #ifdef __APPLE__ char buf[2048] ; bool have_dict = false; -#ifdef ASPELL_FRAMEWORK - char * framework = ASPELL_FRAMEWORK ; + char const * sysdir = lyx::support::package().system_support().absFileName().c_str() ; + char const * userdir = lyx::support::package().user_support().absFileName().c_str() ; + char const * framework = ASPELL_FRAMEWORK ; - if ( strlen(framework) && getPrivateFrameworkPathName(buf, sizeof(buf), framework) ) { - lyx::support::FileName const base(buf); - lyx::support::FileName const data(base.absFileName() + "/Resources/data"); - lyx::support::FileName const dict(base.absFileName() + "/Resources/dict"); + LYXERR(Debug::FILES, "aspell sysdir dir: " << sysdir); + LYXERR(Debug::FILES, "aspell user dir: " << userdir); + have_dict = checkAspellData(config, userdir, ASPELL_FRAMEWORK_DATA, ASPELL_FRAMEWORK_DICT, lang, variety); + if (!have_dict && strlen(framework) && getPrivateFrameworkPathName(buf, sizeof(buf), framework)) { LYXERR(Debug::FILES, "aspell bundle path: " << buf); - have_dict = dict.isDirectory() && data.isDirectory(); - if (have_dict) { - aspell_config_replace(config, "dict-dir", dict.absFileName().c_str()); - aspell_config_replace(config, "data-dir", data.absFileName().c_str()); - LYXERR(Debug::FILES, "aspell dict: " << dict); - } + have_dict = checkAspellData(config, buf, ASPELL_FRAMEWORK_DATA, ASPELL_FRAMEWORK_DICT, lang, variety); } -#endif - if ( !have_dict ) { - lyx::support::FileName const base("/opt/local"); // check for mac-ports data - lyx::support::FileName const data(base.absFileName() + "/lib/aspell-0.60"); - lyx::support::FileName const dict(base.absFileName() + "/share/aspell"); - have_dict = dict.isDirectory() && data.isDirectory(); - if (have_dict) { - aspell_config_replace(config, "dict-dir", dict.absFileName().c_str()); - aspell_config_replace(config, "data-dir", data.absFileName().c_str()); - LYXERR(Debug::FILES, "aspell dict: " << dict); - } + if (!have_dict) { + // check for macports data + have_dict = checkAspellData(config, ASPELL_MACPORTS, ASPELL_MACPORTS_DATA, ASPELL_MACPORTS_DICT, lang, variety); } #endif return config ; @@ -127,13 +184,15 @@ AspellSpeller * AspellChecker::Private::addSpeller(string const & lang, string const & variety) { - AspellConfig * config = getConfig(); + Speller m; + + m.config = getConfig(lang, variety); // Aspell supports both languages and varieties (such as German // old vs. new spelling). The respective naming convention is // lang_REGION-variety (e.g. de_DE-alt). - aspell_config_replace(config, "lang", lang.c_str()); + aspell_config_replace(m.config, "lang", lang.c_str()); if (!variety.empty()) - aspell_config_replace(config, "variety", variety.c_str()); + aspell_config_replace(m.config, "variety", variety.c_str()); // Set the encoding to utf-8. // aspell does also understand "ucs-4", so we would not need a // conversion in theory, but if this is used it expects all @@ -141,31 +200,22 @@ // seems that this uint is not compatible with our char_type on some // platforms (cygwin, OS X). Therefore we use utf-8, that does // always work. - aspell_config_replace(config, "encoding", "utf-8"); + aspell_config_replace(m.config, "encoding", "utf-8"); if (lyxrc.spellchecker_accept_compound) // Consider run-together words as legal compounds - aspell_config_replace(config, "run-together", "true"); + aspell_config_replace(m.config, "run-together", "true"); else // Report run-together words as errors - aspell_config_replace(config, "run-together", "false"); + aspell_config_replace(m.config, "run-together", "false"); - AspellCanHaveError * err = new_aspell_speller(config); - if (spell_error_object) - delete_aspell_can_have_error(spell_error_object); - spell_error_object = 0; + m.e_speller = new_aspell_speller(m.config); + if (aspell_error_number(m.e_speller) != 0) { + // FIXME: We should indicate somehow that this language is not supported. + LYXERR(Debug::FILES, "aspell error: " << aspell_error_message(m.e_speller)); + } - if (aspell_error_number(err) != 0) { - // FIXME: We should we indicate somehow that this language is not - // supported. - spell_error_object = err; - LYXERR(Debug::FILES, "aspell error: " << aspell_error_message(err)); - return 0; - } - Speller m; - m.speller = to_aspell_speller(err); - m.config = config; spellers_[spellerID(lang, variety)] = m; - return m.speller; + return to_aspell_speller(m.e_speller); } @@ -174,7 +224,7 @@ { Spellers::iterator it = spellers_.find(spellerID(lang, variety)); if (it != spellers_.end()) - return it->second.speller; + return to_aspell_speller(it->second.e_speller); return addSpeller(lang, variety); } @@ -224,8 +274,10 @@ { Spellers::iterator it = d->spellers_.find( d->spellerID(word.lang()->code(), word.lang()->variety())); - if (it != d->spellers_.end()) - aspell_speller_add_to_personal(it->second.speller, to_utf8(word.word()).c_str(), -1); + if (it != d->spellers_.end()) { + AspellSpeller * speller = to_aspell_speller(it->second.e_speller); + aspell_speller_add_to_personal(speller, to_utf8(word.word()).c_str(), -1); + } } @@ -233,8 +285,10 @@ { Spellers::iterator it = d->spellers_.find( d->spellerID(word.lang()->code(), word.lang()->variety())); - if (it != d->spellers_.end()) - aspell_speller_add_to_session(it->second.speller, to_utf8(word.word()).c_str(), -1); + if (it != d->spellers_.end()) { + AspellSpeller * speller = to_aspell_speller(it->second.e_speller); + aspell_speller_add_to_session(speller, to_utf8(word.word()).c_str(), -1); + } } @@ -268,46 +322,34 @@ bool AspellChecker::hasDictionary(Language const * lang) const { - if (!lang) - return false; - // code taken from aspell's list-dicts example - AspellConfig * config; - AspellDictInfoList * dlist; - AspellDictInfoEnumeration * dels; - const AspellDictInfo * entry; + bool have = false; + Spellers::iterator it = d->spellers_.begin(); + Spellers::iterator end = d->spellers_.end(); - config = getConfig(); - - /* the returned pointer should _not_ need to be deleted */ - dlist = get_aspell_dict_info_list(config); - - /* config is no longer needed */ - delete_aspell_config(config); - - dels = aspell_dict_info_list_elements(dlist); - - bool have = false; - while ((entry = aspell_dict_info_enumeration_next(dels)) != 0) - { - if (entry->code == lang->code() - && (lang->variety().empty() || entry->jargon == lang->variety())) { - have = true; - break; + if (lang) { + for (; it != end && !have; ++it) { + have = isValidDictionary(it->second.config, lang->code(), lang->variety()); } + if (!have) { + AspellConfig * config = getConfig(lang->code(), lang->variety()); + have = isValidDictionary(config, lang->code(), lang->variety()); + delete_aspell_config(config); + } } - - delete_aspell_dict_info_enumeration(dels); - return have; } docstring const AspellChecker::error() { + Spellers::iterator it = d->spellers_.begin(); + Spellers::iterator end = d->spellers_.end(); char const * err = 0; - if (d->spell_error_object && aspell_error_number(d->spell_error_object) != 0) - err = aspell_error_message(d->spell_error_object); + for (; it != end && 0 == err; ++it) { + if (it->second.e_speller && aspell_error_number(it->second.e_speller) != 0) + err = aspell_error_message(it->second.e_speller); + } // FIXME UNICODE: err is not in UTF8, but probably the locale encoding return (err ? from_utf8(err) : docstring()); Index: config/spell.m4 =================================================================== --- config/spell.m4 (Revision 34532) +++ config/spell.m4 (Arbeitskopie) @@ -17,6 +17,9 @@ AC_MSG_RESULT(yes) AC_DEFINE(USE_ASPELL, 1, [Define as 1 to use the aspell library]) lyx_flags="$lyx_flags use-aspell" + ### Check for aspell framework name + LYX_WITH_DIR([aspell-framework], [name of aspell framework],aspell_framework, []) + AC_DEFINE_UNQUOTED([ASPELL_FRAMEWORK], "${lyx_cv_aspell_framework}", [Define as name of aspell framework]) else AC_MSG_RESULT(no) fi @@ -74,16 +77,6 @@ CHECK_WITH_ASPELL AM_CONDITIONAL(USE_ASPELL, $lyx_use_aspell) - if $lyx_use_aspell ; then -### Check for aspell framework name - LYX_WITH_DIR([aspell-framework],[name of aspell framework],aspell_framework, NONE) - if ! test "x${lyx_cv_aspell_framework}" = xNONE; then - AC_DEFINE_UNQUOTED([ASPELL_FRAMEWORK], "${lyx_cv_aspell_framework}") - else - AC_DEFINE(ASPELL_FRAMEWORK, [""], [Define as name of aspell framework]) - fi - fi - CHECK_WITH_ENCHANT AM_CONDITIONAL(USE_ENCHANT, $lyx_use_enchant)