diff options
Diffstat (limited to 'editeng/source/misc/unolingu.cxx')
-rw-r--r-- | editeng/source/misc/unolingu.cxx | 1374 |
1 files changed, 1374 insertions, 0 deletions
diff --git a/editeng/source/misc/unolingu.cxx b/editeng/source/misc/unolingu.cxx new file mode 100644 index 000000000000..e95ce560023b --- /dev/null +++ b/editeng/source/misc/unolingu.cxx @@ -0,0 +1,1374 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_editeng.hxx" + +#include <map> +#include <set> +#include <vector> +#include <slist> +#include <memory> +#include <editeng/unolingu.hxx> +#include <tools/debug.hxx> +#include <tools/urlobj.hxx> +#include <rtl/logfile.hxx> +#include <unotools/pathoptions.hxx> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/frame/XStorable.hpp> +#include <com/sun/star/lang/XEventListener.hpp> +#include <com/sun/star/linguistic2/XAvailableLocales.hpp> +#include <com/sun/star/ucb/XAnyCompareFactory.hpp> +#include <com/sun/star/ucb/XContentAccess.hpp> +#include <com/sun/star/ucb/XSortedDynamicResultSetFactory.hpp> +#include <com/sun/star/ucb/NumberedSortingInfo.hpp> +#include <com/sun/star/ucb/XContentAccess.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/util/DateTime.hpp> + +#include <comphelper/processfactory.hxx> +#include <cppuhelper/implbase1.hxx> // helper for implementations +#include <i18npool/mslangid.hxx> +#include <unotools/lingucfg.hxx> +#include <unotools/ucbhelper.hxx> +#include <unotools/localfilehelper.hxx> +#include <ucbhelper/commandenvironment.hxx> +#include <ucbhelper/content.hxx> +#include <comphelper/processfactory.hxx> +#include <vcl/msgbox.hxx> +#include <tools/shl.hxx> +#include <linguistic/misc.hxx> +#include <editeng/eerdll.hxx> +#include <editeng/editrids.hrc> + +using namespace ::rtl; +using namespace ::comphelper; +using namespace ::linguistic; +using namespace ::com::sun::star; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::linguistic2; + +#define CSS com::sun::star + +/////////////////////////////////////////////////////////////////////////// + + +static uno::Reference< XLinguServiceManager > GetLngSvcMgr_Impl() +{ + uno::Reference< XLinguServiceManager > xRes; + uno::Reference< XMultiServiceFactory > xMgr = getProcessServiceFactory(); + if (xMgr.is()) + { + xRes = uno::Reference< XLinguServiceManager > ( xMgr->createInstance( + OUString( RTL_CONSTASCII_USTRINGPARAM( + "com.sun.star.linguistic2.LinguServiceManager" ) ) ), UNO_QUERY ) ; + } + return xRes; +} + +/////////////////////////////////////////////////////////////////////////// + +BOOL lcl_FindEntry( const OUString &rEntry, const Sequence< OUString > &rCfgSvcs ) +{ + INT32 nRes = -1; + INT32 nEntries = rCfgSvcs.getLength(); + const OUString *pEntry = rCfgSvcs.getConstArray(); + for (INT32 i = 0; i < nEntries && nRes == -1; ++i) + { + if (rEntry == pEntry[i]) + nRes = i; + } + return nRes != -1; +} + + +Sequence< OUString > lcl_RemoveMissingEntries( + const Sequence< OUString > &rCfgSvcs, + const Sequence< OUString > &rAvailSvcs ) +{ + Sequence< OUString > aRes( rCfgSvcs.getLength() ); + OUString *pRes = aRes.getArray(); + INT32 nCnt = 0; + + INT32 nEntries = rCfgSvcs.getLength(); + const OUString *pEntry = rCfgSvcs.getConstArray(); + for (INT32 i = 0; i < nEntries; ++i) + { + if (pEntry[i].getLength() && lcl_FindEntry( pEntry[i], rAvailSvcs )) + pRes[ nCnt++ ] = pEntry[i]; + } + + aRes.realloc( nCnt ); + return aRes; +} + + +Sequence< OUString > lcl_GetLastFoundSvcs( + SvtLinguConfig &rCfg, + const OUString &rLastFoundList , + const Locale &rAvailLocale ) +{ + Sequence< OUString > aRes; + + OUString aCfgLocaleStr( MsLangId::convertLanguageToIsoString( + SvxLocaleToLanguage( rAvailLocale ) ) ); + + Sequence< OUString > aNodeNames( rCfg.GetNodeNames(rLastFoundList) ); + BOOL bFound = lcl_FindEntry( aCfgLocaleStr, aNodeNames); + + if (bFound) + { + Sequence< OUString > aNames(1); + OUString &rNodeName = aNames.getArray()[0]; + rNodeName = rLastFoundList; + rNodeName += OUString::valueOf( (sal_Unicode)'/' ); + rNodeName += aCfgLocaleStr; + Sequence< Any > aValues( rCfg.GetProperties( aNames ) ); +#if OSL_DEBUG_LEVEL > 1 + const Any *pValue; + pValue = aValues.getConstArray(); +#endif + if (aValues.getLength()) + { + DBG_ASSERT( aValues.getLength() == 1, "unexpected length of sequence" ); + Sequence< OUString > aSvcImplNames; + if (aValues.getConstArray()[0] >>= aSvcImplNames) + aRes = aSvcImplNames; + else + { + DBG_ERROR( "type mismatch" ); + } + } + } + + return aRes; +} + + +Sequence< OUString > lcl_GetNewEntries( + const Sequence< OUString > &rLastFoundSvcs, + const Sequence< OUString > &rAvailSvcs ) +{ + INT32 nLen = rAvailSvcs.getLength(); + Sequence< OUString > aRes( nLen ); + OUString *pRes = aRes.getArray(); + INT32 nCnt = 0; + + const OUString *pEntry = rAvailSvcs.getConstArray(); + for (INT32 i = 0; i < nLen; ++i) + { + if (pEntry[i].getLength() && !lcl_FindEntry( pEntry[i], rLastFoundSvcs )) + pRes[ nCnt++ ] = pEntry[i]; + } + + aRes.realloc( nCnt ); + return aRes; +} + + +Sequence< OUString > lcl_MergeSeq( + const Sequence< OUString > &rCfgSvcs, + const Sequence< OUString > &rNewSvcs ) +{ + Sequence< OUString > aRes( rCfgSvcs.getLength() + rNewSvcs.getLength() ); + OUString *pRes = aRes.getArray(); + INT32 nCnt = 0; + + for (INT32 k = 0; k < 2; ++k) + { + // add previously configuerd service first and append + // new found services at the end + const Sequence< OUString > &rSeq = k == 0 ? rCfgSvcs : rNewSvcs; + + INT32 nLen = rSeq.getLength(); + const OUString *pEntry = rSeq.getConstArray(); + for (INT32 i = 0; i < nLen; ++i) + { + if (pEntry[i].getLength() && !lcl_FindEntry( pEntry[i], aRes )) + pRes[ nCnt++ ] = pEntry[i]; + } + } + + aRes.realloc( nCnt ); + return aRes; +} + +/////////////////////////////////////////////////////////////////////////// + +// static member initialization +INT16 SvxLinguConfigUpdate::nNeedUpdating = -1; +INT32 SvxLinguConfigUpdate::nCurrentDataFilesChangedCheckValue = -1; + +void SvxLinguConfigUpdate::UpdateAll( sal_Bool bForceCheck ) +{ + RTL_LOGFILE_CONTEXT( aLog, "svx: SvxLinguConfigUpdate::UpdateAll" ); + + if (IsNeedUpdateAll( bForceCheck )) + { + typedef OUString OUstring_t; + typedef Sequence< OUString > Sequence_OUString_t; + typedef std::vector< OUstring_t > OUString_vector_t; + typedef std::set< OUstring_t > OUString_set_t; + std::vector< OUString_vector_t > aVector; + typedef std::map< OUstring_t, Sequence_OUString_t > list_entry_map_t; + + RTL_LOGFILE_CONTEXT( aLog, "svx: SvxLinguConfigUpdate::UpdateAll - updating..." ); + + DBG_ASSERT( nNeedUpdating == 1, "SvxLinguConfigUpdate::UpdateAll already updated!" ); + + uno::Reference< XLinguServiceManager > xLngSvcMgr( GetLngSvcMgr_Impl() ); + DBG_ASSERT( xLngSvcMgr.is(), "service manager missing"); + if (!xLngSvcMgr.is()) + return; + + SvtLinguConfig aCfg; + + const int nNumServices = 4; + const sal_Char * apServices[nNumServices] = { SN_SPELLCHECKER, SN_GRAMMARCHECKER, SN_HYPHENATOR, SN_THESAURUS }; + const sal_Char * apCurLists[nNumServices] = { "ServiceManager/SpellCheckerList", "ServiceManager/GrammarCheckerList", "ServiceManager/HyphenatorList", "ServiceManager/ThesaurusList" }; + const sal_Char * apLastFoundLists[nNumServices] = { "ServiceManager/LastFoundSpellCheckers", "ServiceManager/LastFoundGrammarCheckers", "ServiceManager/LastFoundHyphenators", "ServiceManager/LastFoundThesauri" }; + + // usage of indices as above: 0 = spell checker, 1 = grammar checker, 2 = hyphenator, 3 = thesaurus + std::vector< list_entry_map_t > aLastFoundSvcs(nNumServices); + std::vector< list_entry_map_t > aCurSvcs(nNumServices); + + for (int k = 0; k < nNumServices; ++k) + { + OUString aService( A2OU( apServices[k] ) ); + OUString aActiveList( A2OU( apCurLists[k] ) ); + OUString aLastFoundList( A2OU( apLastFoundLists[k] ) ); + INT32 i; + + // + // remove configured but not available language/services entries + // + Sequence< OUString > aNodeNames( aCfg.GetNodeNames( aActiveList ) ); // list of configured locales + INT32 nNodeNames = aNodeNames.getLength(); + const OUString *pNodeName = aNodeNames.getConstArray(); + for (i = 0; i < nNodeNames; ++i) + { + Locale aLocale( SvxCreateLocale( MsLangId::convertIsoStringToLanguage(pNodeName[i]) ) ); + Sequence< OUString > aCfgSvcs( + xLngSvcMgr->getConfiguredServices( aService, aLocale )); + Sequence< OUString > aAvailSvcs( + xLngSvcMgr->getAvailableServices( aService, aLocale )); +#if OSL_DEBUG_LEVEL > 1 + const OUString * pCfgSvcs = aCfgSvcs.getConstArray();; + const OUString * pAvailSvcs = aAvailSvcs.getConstArray();; + (void) pCfgSvcs; + (void) pAvailSvcs; +#endif + aCfgSvcs = lcl_RemoveMissingEntries( aCfgSvcs, aAvailSvcs ); + + aCurSvcs[k][ pNodeName[i] ] = aCfgSvcs; + } + + // + // add new available language/servcice entries + // + uno::Reference< XAvailableLocales > xAvail( xLngSvcMgr, UNO_QUERY ); + Sequence< Locale > aAvailLocales( xAvail->getAvailableLocales(aService) ); + INT32 nAvailLocales = aAvailLocales.getLength(); + const Locale *pAvailLocale = aAvailLocales.getConstArray(); + for (i = 0; i < nAvailLocales; ++i) + { + Sequence< OUString > aAvailSvcs( + xLngSvcMgr->getAvailableServices( aService, pAvailLocale[i] )); + Sequence< OUString > aLastSvcs( + lcl_GetLastFoundSvcs( aCfg, aLastFoundList , pAvailLocale[i] )); + Sequence< OUString > aNewSvcs = + lcl_GetNewEntries( aLastSvcs, aAvailSvcs ); +#if OSL_DEBUG_LEVEL > 1 + const OUString * pAvailSvcs = aAvailSvcs.getConstArray(); + const OUString * pLastSvcs = aLastSvcs.getConstArray(); + const OUString * pNewSvcs = aNewSvcs.getConstArray(); + (void) pAvailSvcs; + (void) pLastSvcs; + (void) pNewSvcs; +#endif + + OUString aCfgLocaleStr( MsLangId::convertLanguageToIsoString( + SvxLocaleToLanguage( pAvailLocale[i] ) ) ); + Sequence< OUString > aCfgSvcs( aCurSvcs[k][ aCfgLocaleStr ] ); + + // merge services list (previously configured to be listed first). + aCfgSvcs = lcl_MergeSeq( aCfgSvcs, aNewSvcs ); + +/* + // there is at most one Hyphenator per language allowed + // to be configured, thus we only use the first one found. + if (k == 2 && aCfgSvcs.getLength() > 1) + aCfgSvcs.realloc(1); +*/ + aCurSvcs[k][ aCfgLocaleStr ] = aCfgSvcs; + } + + // + // set last found services to currently available ones + // + for (i = 0; i < nAvailLocales; ++i) + { + Sequence< OUString > aSvcImplNames( + xLngSvcMgr->getAvailableServices( aService, pAvailLocale[i] ) ); + +#if OSL_DEBUG_LEVEL > 1 + INT32 nSvcs = aSvcImplNames.getLength(); + const OUString *pSvcImplName = aSvcImplNames.getConstArray(); + for (INT32 j = 0; j < nSvcs; ++j) + { + OUString aImplName( pSvcImplName[j] ); + } +#endif + + OUString aCfgLocaleStr( MsLangId::convertLanguageToIsoString( + SvxLocaleToLanguage( pAvailLocale[i] ) ) ); + aLastFoundSvcs[k][ aCfgLocaleStr ] = aSvcImplNames; + } + } + + // + // write new data back to configuration + // + for (int k = 0; k < nNumServices; ++k) + { + for (int i = 0; i < 2; ++i) + { + const sal_Char *pSubNodeName = (i == 0) ? apCurLists[k] : apLastFoundLists[k]; + OUString aSubNodeName( A2OU(pSubNodeName) ); + + list_entry_map_t &rCurMap = (i == 0) ? aCurSvcs[k] : aLastFoundSvcs[k]; + list_entry_map_t::const_iterator aIt( rCurMap.begin() ); + sal_Int32 nVals = static_cast< sal_Int32 >( rCurMap.size() ); + Sequence< PropertyValue > aNewValues( nVals ); + PropertyValue *pNewValue = aNewValues.getArray(); + while (aIt != rCurMap.end()) + { + OUString aCfgEntryName( aSubNodeName ); + aCfgEntryName += OUString::valueOf( (sal_Unicode) '/' ); + aCfgEntryName += (*aIt).first; + +#if OSL_DEBUG_LEVEL > 1 + Sequence< OUString > aSvcImplNames( (*aIt).second ); + INT32 nSvcs = aSvcImplNames.getLength(); + const OUString *pSvcImplName = aSvcImplNames.getConstArray(); + for (INT32 j = 0; j < nSvcs; ++j) + { + OUString aImplName( pSvcImplName[j] ); + } +#endif + pNewValue->Name = aCfgEntryName; + pNewValue->Value <<= (*aIt).second; + ++pNewValue; + ++aIt; + } + DBG_ASSERT( pNewValue - aNewValues.getArray() == nVals, + "possible mismatch of sequence size and property number" ); + + { + RTL_LOGFILE_CONTEXT( aLog, "svx: SvxLinguConfigUpdate::UpdateAll - ReplaceSetProperties" ); + // add new or replace existing entries. + BOOL bRes = aCfg.ReplaceSetProperties( aSubNodeName, aNewValues ); + if (!bRes) + { +#if OSL_DEBUG_LEVEL > 1 + DBG_ERROR( "failed to set new configuration values" ); +#endif + } + } + } + } + DBG_ASSERT( nCurrentDataFilesChangedCheckValue != -1, "SvxLinguConfigUpdate::UpdateAll DataFilesChangedCheckValue not yet calculated!" ); + Any aAny; + + // for the time being (developer builds until OOo 3.0) + // we should always check for everything available + // otherwise we may miss a new installed extension dicitonary + // just because e.g. the spellchecker is not asked what + // languages it does support currently... + // Since the check is on-demand occuring and executed once it should + // not be too troublesome. + // In OOo 3.0 we will not need the respective code anymore at all. +// aAny <<= nCurrentDataFilesChangedCheckValue; + aAny <<= (INT32) -1; // keep the value set to 'need to check' + + aCfg.SetProperty( A2OU( "DataFilesChangedCheckValue" ), aAny ); + + //! Note 1: the new values are commited when the 'aCfg' object + //! gets destroyed. + //! Note 2: the new settings in the configuration get applied + //! because the 'LngSvcMgr' (in linguistic/source/lngsvcmgr.hxx) + //! listens to the configuration for changes of the relevant + //! properties and then applies the new settings. + + // nothing needs to be done anymore + nNeedUpdating = 0; + } +} + + +INT32 SvxLinguConfigUpdate::CalcDataFilesChangedCheckValue() +{ + RTL_LOGFILE_CONTEXT( aLog, "svx: SvxLinguConfigUpdate::CalcDataFilesChangedCheckValue" ); + + INT32 nHashVal = 0; + // nothing to be checked anymore since those old directory paths are gone by now + return nHashVal; +} + + +BOOL SvxLinguConfigUpdate::IsNeedUpdateAll( sal_Bool bForceCheck ) +{ + RTL_LOGFILE_CONTEXT( aLog, "svx: SvxLinguConfigUpdate::IsNeedUpdateAll" ); + if (nNeedUpdating == -1 || bForceCheck ) // need to check if updating is necessary + { + // calculate hash value for current data files + nCurrentDataFilesChangedCheckValue = CalcDataFilesChangedCheckValue(); + + // compare hash value and check value to see if anything has changed + // and thus the configuration needs to be updated + SvtLinguOptions aLinguOpt; + SvtLinguConfig aCfg; + aCfg.GetOptions( aLinguOpt ); + nNeedUpdating = (nCurrentDataFilesChangedCheckValue == aLinguOpt.nDataFilesChangedCheckValue) ? 0 : 1; + } + DBG_ASSERT( nNeedUpdating != -1, + "need for linguistic configuration update should have been already checked." ); + + return nNeedUpdating == 1; +} + +/////////////////////////////////////////////////////////////////////////// + + +//! Dummy implementation in order to avoid loading of lingu DLL +//! when only the XSupportedLocales interface is used. +//! The dummy accesses the real implementation (and thus loading the DLL) +//! when "real" work needs to be done only. +class ThesDummy_Impl : + public cppu::WeakImplHelper1< XThesaurus > +{ + uno::Reference< XThesaurus > xThes; // the real one... + Sequence< Locale > *pLocaleSeq; + + void GetCfgLocales(); + + void GetThes_Impl(); + +public: + ThesDummy_Impl() : pLocaleSeq(0) {} + ~ThesDummy_Impl(); + + // XSupportedLocales + virtual ::com::sun::star::uno::Sequence< + ::com::sun::star::lang::Locale > SAL_CALL + getLocales() + throw(::com::sun::star::uno::RuntimeException); + virtual sal_Bool SAL_CALL + hasLocale( const ::com::sun::star::lang::Locale& rLocale ) + throw(::com::sun::star::uno::RuntimeException); + + // XThesaurus + virtual ::com::sun::star::uno::Sequence< + ::com::sun::star::uno::Reference< + ::com::sun::star::linguistic2::XMeaning > > SAL_CALL + queryMeanings( const ::rtl::OUString& rTerm, + const ::com::sun::star::lang::Locale& rLocale, + const ::com::sun::star::beans::PropertyValues& rProperties ) + throw(::com::sun::star::lang::IllegalArgumentException, + ::com::sun::star::uno::RuntimeException); +}; + + +ThesDummy_Impl::~ThesDummy_Impl() +{ + delete pLocaleSeq; +} + + +void ThesDummy_Impl::GetCfgLocales() +{ + if (!pLocaleSeq) + { + SvtLinguConfig aCfg; + String aNode( A2OU( "ServiceManager/ThesaurusList" ) ); + Sequence < OUString > aNodeNames( aCfg.GetNodeNames( aNode ) ); + const OUString *pNodeNames = aNodeNames.getConstArray(); + INT32 nLen = aNodeNames.getLength(); + pLocaleSeq = new Sequence< Locale >( nLen ); + Locale *pLocale = pLocaleSeq->getArray(); + for (INT32 i = 0; i < nLen; ++i) + { + pLocale[i] = SvxCreateLocale( + MsLangId::convertIsoStringToLanguage( pNodeNames[i] ) ); + } + } +} + + +void ThesDummy_Impl::GetThes_Impl() +{ + // update configuration before accessing the service + if (SvxLinguConfigUpdate::IsNeedUpdateAll()) + SvxLinguConfigUpdate::UpdateAll(); + + if (!xThes.is()) + { + uno::Reference< XLinguServiceManager > xLngSvcMgr( GetLngSvcMgr_Impl() ); + if (xLngSvcMgr.is()) + xThes = xLngSvcMgr->getThesaurus(); + + if (xThes.is()) + { + // no longer needed... + delete pLocaleSeq; pLocaleSeq = 0; + } + } +} + + +uno::Sequence< lang::Locale > SAL_CALL + ThesDummy_Impl::getLocales() + throw(uno::RuntimeException) +{ + if (!SvxLinguConfigUpdate::IsNeedUpdateAll()) // configuration already update and thus lingu DLL's already loaded ? + GetThes_Impl(); + if (xThes.is()) + return xThes->getLocales(); + else if (!pLocaleSeq) // if not already loaded save startup time by avoiding loading them now + GetCfgLocales(); + return *pLocaleSeq; +} + + +sal_Bool SAL_CALL + ThesDummy_Impl::hasLocale( const lang::Locale& rLocale ) + throw(uno::RuntimeException) +{ + if (!SvxLinguConfigUpdate::IsNeedUpdateAll()) // configuration already update and thus lingu DLL's already loaded ? + GetThes_Impl(); + if (xThes.is()) + return xThes->hasLocale( rLocale ); + else if (!pLocaleSeq) // if not already loaded save startup time by avoiding loading them now + GetCfgLocales(); + GetCfgLocales(); + BOOL bFound = FALSE; + INT32 nLen = pLocaleSeq->getLength(); + const Locale *pLocale = pLocaleSeq->getConstArray(); + const Locale *pEnd = pLocale + nLen; + for ( ; pLocale < pEnd && !bFound; ++pLocale) + { + bFound = pLocale->Language == rLocale.Language && + pLocale->Country == rLocale.Country && + pLocale->Variant == rLocale.Variant; + } + return bFound; +} + + +uno::Sequence< uno::Reference< linguistic2::XMeaning > > SAL_CALL + ThesDummy_Impl::queryMeanings( + const rtl::OUString& rTerm, + const lang::Locale& rLocale, + const beans::PropertyValues& rProperties ) + throw(lang::IllegalArgumentException, + uno::RuntimeException) +{ + GetThes_Impl(); + uno::Sequence< uno::Reference< linguistic2::XMeaning > > aRes; + DBG_ASSERT( xThes.is(), "Thesaurus missing" ); + if (xThes.is()) + aRes = xThes->queryMeanings( rTerm, rLocale, rProperties ); + return aRes; +} + + +/////////////////////////////////////////////////////////////////////////// + + +//! Dummy implementation in order to avoid loading of lingu DLL. +//! The dummy accesses the real implementation (and thus loading the DLL) +//! when it needs to be done only. +class SpellDummy_Impl : + public cppu::WeakImplHelper1< XSpellChecker1 > +{ + uno::Reference< XSpellChecker1 > xSpell; // the real one... + + void GetSpell_Impl(); + +public: + + // XSupportedLanguages (for XSpellChecker1) + virtual ::com::sun::star::uno::Sequence< sal_Int16 > SAL_CALL + getLanguages() + throw(::com::sun::star::uno::RuntimeException); + virtual sal_Bool SAL_CALL + hasLanguage( sal_Int16 nLanguage ) + throw(::com::sun::star::uno::RuntimeException); + + // XSpellChecker1 (same as XSpellChecker but sal_Int16 for language) + virtual sal_Bool SAL_CALL + isValid( const ::rtl::OUString& rWord, sal_Int16 nLanguage, + const ::com::sun::star::beans::PropertyValues& rProperties ) + throw(::com::sun::star::lang::IllegalArgumentException, + ::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Reference< + ::com::sun::star::linguistic2::XSpellAlternatives > SAL_CALL + spell( const ::rtl::OUString& rWord, sal_Int16 nLanguage, + const ::com::sun::star::beans::PropertyValues& rProperties ) + throw(::com::sun::star::lang::IllegalArgumentException, + ::com::sun::star::uno::RuntimeException); +}; + + +void SpellDummy_Impl::GetSpell_Impl() +{ + // update configuration before accessing the service + if (SvxLinguConfigUpdate::IsNeedUpdateAll()) + SvxLinguConfigUpdate::UpdateAll(); + + if (!xSpell.is()) + { + uno::Reference< XLinguServiceManager > xLngSvcMgr( GetLngSvcMgr_Impl() ); + if (xLngSvcMgr.is()) + xSpell = uno::Reference< XSpellChecker1 >( xLngSvcMgr->getSpellChecker(), UNO_QUERY ); + } +} + + +uno::Sequence< sal_Int16 > SAL_CALL + SpellDummy_Impl::getLanguages() + throw(uno::RuntimeException) +{ + GetSpell_Impl(); + if (xSpell.is()) + return xSpell->getLanguages(); + else + return uno::Sequence< sal_Int16 >(); +} + + +sal_Bool SAL_CALL + SpellDummy_Impl::hasLanguage( sal_Int16 nLanguage ) + throw(uno::RuntimeException) +{ + GetSpell_Impl(); + BOOL bRes = FALSE; + if (xSpell.is()) + bRes = xSpell->hasLanguage( nLanguage ); + return bRes; +} + + +sal_Bool SAL_CALL + SpellDummy_Impl::isValid( const rtl::OUString& rWord, sal_Int16 nLanguage, + const beans::PropertyValues& rProperties ) + throw(lang::IllegalArgumentException, + uno::RuntimeException) +{ + GetSpell_Impl(); + BOOL bRes = TRUE; + if (xSpell.is()) + bRes = xSpell->isValid( rWord, nLanguage, rProperties ); + return bRes; +} + + +uno::Reference< linguistic2::XSpellAlternatives > SAL_CALL + SpellDummy_Impl::spell( const rtl::OUString& rWord, sal_Int16 nLanguage, + const beans::PropertyValues& rProperties ) + throw(lang::IllegalArgumentException, + uno::RuntimeException) +{ + GetSpell_Impl(); + uno::Reference< linguistic2::XSpellAlternatives > xRes; + if (xSpell.is()) + xRes = xSpell->spell( rWord, nLanguage, rProperties ); + return xRes; +} + + +/////////////////////////////////////////////////////////////////////////// + + +//! Dummy implementation in order to avoid loading of lingu DLL. +//! The dummy accesses the real implementation (and thus loading the DLL) +//! when it needs to be done only. +class HyphDummy_Impl : + public cppu::WeakImplHelper1< XHyphenator > +{ + uno::Reference< XHyphenator > xHyph; // the real one... + + void GetHyph_Impl(); + +public: + + // XSupportedLocales + virtual ::com::sun::star::uno::Sequence< + ::com::sun::star::lang::Locale > SAL_CALL + getLocales() + throw(::com::sun::star::uno::RuntimeException); + virtual sal_Bool SAL_CALL + hasLocale( const ::com::sun::star::lang::Locale& rLocale ) + throw(::com::sun::star::uno::RuntimeException); + + // XHyphenator + virtual ::com::sun::star::uno::Reference< + ::com::sun::star::linguistic2::XHyphenatedWord > SAL_CALL + hyphenate( const ::rtl::OUString& rWord, + const ::com::sun::star::lang::Locale& rLocale, + sal_Int16 nMaxLeading, + const ::com::sun::star::beans::PropertyValues& rProperties ) + throw(::com::sun::star::lang::IllegalArgumentException, + ::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Reference< + ::com::sun::star::linguistic2::XHyphenatedWord > SAL_CALL + queryAlternativeSpelling( const ::rtl::OUString& rWord, + const ::com::sun::star::lang::Locale& rLocale, + sal_Int16 nIndex, + const ::com::sun::star::beans::PropertyValues& rProperties ) + throw(::com::sun::star::lang::IllegalArgumentException, + ::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Reference< + ::com::sun::star::linguistic2::XPossibleHyphens > SAL_CALL + createPossibleHyphens( + const ::rtl::OUString& rWord, + const ::com::sun::star::lang::Locale& rLocale, + const ::com::sun::star::beans::PropertyValues& rProperties ) + throw(::com::sun::star::lang::IllegalArgumentException, + ::com::sun::star::uno::RuntimeException); +}; + + +void HyphDummy_Impl::GetHyph_Impl() +{ + // update configuration before accessing the service + if (SvxLinguConfigUpdate::IsNeedUpdateAll()) + SvxLinguConfigUpdate::UpdateAll(); + + if (!xHyph.is()) + { + uno::Reference< XLinguServiceManager > xLngSvcMgr( GetLngSvcMgr_Impl() ); + if (xLngSvcMgr.is()) + xHyph = xLngSvcMgr->getHyphenator(); + } +} + + +uno::Sequence< lang::Locale > SAL_CALL + HyphDummy_Impl::getLocales() + throw(uno::RuntimeException) +{ + GetHyph_Impl(); + if (xHyph.is()) + return xHyph->getLocales(); + else + return uno::Sequence< lang::Locale >(); +} + + +sal_Bool SAL_CALL + HyphDummy_Impl::hasLocale( const lang::Locale& rLocale ) + throw(uno::RuntimeException) +{ + GetHyph_Impl(); + BOOL bRes = FALSE; + if (xHyph.is()) + bRes = xHyph->hasLocale( rLocale ); + return bRes; +} + + +uno::Reference< linguistic2::XHyphenatedWord > SAL_CALL + HyphDummy_Impl::hyphenate( + const rtl::OUString& rWord, + const lang::Locale& rLocale, + sal_Int16 nMaxLeading, + const beans::PropertyValues& rProperties ) + throw(lang::IllegalArgumentException, + uno::RuntimeException) +{ + GetHyph_Impl(); + uno::Reference< linguistic2::XHyphenatedWord > xRes; + if (xHyph.is()) + xRes = xHyph->hyphenate( rWord, rLocale, nMaxLeading, rProperties ); + return xRes; +} + + +uno::Reference< linguistic2::XHyphenatedWord > SAL_CALL + HyphDummy_Impl::queryAlternativeSpelling( + const rtl::OUString& rWord, + const lang::Locale& rLocale, + sal_Int16 nIndex, + const PropertyValues& rProperties ) + throw(lang::IllegalArgumentException, + uno::RuntimeException) +{ + GetHyph_Impl(); + uno::Reference< linguistic2::XHyphenatedWord > xRes; + if (xHyph.is()) + xRes = xHyph->queryAlternativeSpelling( rWord, rLocale, nIndex, rProperties ); + return xRes; +} + + +uno::Reference< linguistic2::XPossibleHyphens > SAL_CALL + HyphDummy_Impl::createPossibleHyphens( + const rtl::OUString& rWord, + const lang::Locale& rLocale, + const beans::PropertyValues& rProperties ) + throw(lang::IllegalArgumentException, + uno::RuntimeException) +{ + GetHyph_Impl(); + uno::Reference< linguistic2::XPossibleHyphens > xRes; + if (xHyph.is()) + xRes = xHyph->createPossibleHyphens( rWord, rLocale, rProperties ); + return xRes; +} + + +/////////////////////////////////////////////////////////////////////////// + + +typedef cppu::WeakImplHelper1 < XEventListener > LinguMgrAppExitLstnrBaseClass; + +class LinguMgrAppExitLstnr : public LinguMgrAppExitLstnrBaseClass +{ + uno::Reference< XComponent > xDesktop; + +public: + LinguMgrAppExitLstnr(); + virtual ~LinguMgrAppExitLstnr(); + + virtual void AtExit() = 0; + + + // lang::XEventListener + virtual void SAL_CALL disposing(const EventObject& rSource) + throw( RuntimeException ); +}; + +LinguMgrAppExitLstnr::LinguMgrAppExitLstnr() +{ + // add object to frame::Desktop EventListeners in order to properly call + // the AtExit function at appliction exit. + + uno::Reference< XMultiServiceFactory > xMgr = getProcessServiceFactory(); + if ( xMgr.is() ) + { + xDesktop = uno::Reference< XComponent > ( xMgr->createInstance( + OUString( RTL_CONSTASCII_USTRINGPARAM ( "com.sun.star.frame.Desktop" ) ) ), UNO_QUERY ) ; + if (xDesktop.is()) + xDesktop->addEventListener( this ); + } +} + +LinguMgrAppExitLstnr::~LinguMgrAppExitLstnr() +{ + if (xDesktop.is()) + { + xDesktop->removeEventListener( this ); + xDesktop = NULL; //! release reference to desktop + } + DBG_ASSERT(!xDesktop.is(), "reference to desktop should be realeased"); +} + +void LinguMgrAppExitLstnr::disposing(const EventObject& rSource) + throw( RuntimeException ) +{ + if (xDesktop.is() && rSource.Source == xDesktop) + { + xDesktop->removeEventListener( this ); + xDesktop = NULL; //! release reference to desktop + + AtExit(); + } +} + +/////////////////////////////////////////////////////////////////////////// + +class LinguMgrExitLstnr : public LinguMgrAppExitLstnr +{ +public: + virtual void AtExit(); +}; + +void LinguMgrExitLstnr::AtExit() +{ + // release references + LinguMgr::xLngSvcMgr = 0; + LinguMgr::xSpell = 0; + LinguMgr::xHyph = 0; + LinguMgr::xThes = 0; + LinguMgr::xDicList = 0; + LinguMgr::xProp = 0; + LinguMgr::xIgnoreAll = 0; + LinguMgr::xChangeAll = 0; + + LinguMgr::bExiting = sal_True; + + //TL:TODO: MBA fragen wie ich ohne Absturz hier meinen Speicher + // wieder freibekomme... + //delete LinguMgr::pExitLstnr; + LinguMgr::pExitLstnr = 0; +} + +/////////////////////////////////////////////////////////////////////////// + + +// static member initialization +LinguMgrExitLstnr * LinguMgr::pExitLstnr = 0; +sal_Bool LinguMgr::bExiting = sal_False; +uno::Reference< XLinguServiceManager > LinguMgr::xLngSvcMgr = 0; +uno::Reference< XSpellChecker1 > LinguMgr::xSpell = 0; +uno::Reference< XHyphenator > LinguMgr::xHyph = 0; +uno::Reference< XThesaurus > LinguMgr::xThes = 0; +uno::Reference< XDictionaryList > LinguMgr::xDicList = 0; +uno::Reference< XPropertySet > LinguMgr::xProp = 0; +uno::Reference< XDictionary > LinguMgr::xIgnoreAll = 0; +uno::Reference< XDictionary > LinguMgr::xChangeAll = 0; + + +uno::Reference< XLinguServiceManager > LinguMgr::GetLngSvcMgr() +{ + if (bExiting) + return 0; + + if (!pExitLstnr) + pExitLstnr = new LinguMgrExitLstnr; + + if (!xLngSvcMgr.is()) + xLngSvcMgr = GetLngSvcMgr_Impl(); + + return xLngSvcMgr; +} + + +uno::Reference< XSpellChecker1 > LinguMgr::GetSpellChecker() +{ + return xSpell.is() ? xSpell : GetSpell(); +} + +uno::Reference< XHyphenator > LinguMgr::GetHyphenator() +{ + return xHyph.is() ? xHyph : GetHyph(); +} + +uno::Reference< XThesaurus > LinguMgr::GetThesaurus() +{ + return xThes.is() ? xThes : GetThes(); +} + +uno::Reference< XDictionaryList > LinguMgr::GetDictionaryList() +{ + return xDicList.is() ? xDicList : GetDicList(); +} + +uno::Reference< XPropertySet > LinguMgr::GetLinguPropertySet() +{ + return xProp.is() ? xProp : GetProp(); +} + +uno::Reference< XDictionary > LinguMgr::GetStandardDic() +{ + //! don't hold reference to this + //! (it may be removed from dictionary list and needs to be + //! created empty if accessed again) + return GetStandard(); +} + +uno::Reference< XDictionary > LinguMgr::GetIgnoreAllList() +{ + return xIgnoreAll.is() ? xIgnoreAll : GetIgnoreAll(); +} + +uno::Reference< XDictionary > LinguMgr::GetChangeAllList() +{ + return xChangeAll.is() ? xChangeAll : GetChangeAll(); +} + +uno::Reference< XSpellChecker1 > LinguMgr::GetSpell() +{ + if (bExiting) + return 0; + + if (!pExitLstnr) + pExitLstnr = new LinguMgrExitLstnr; + + //! use dummy implementation in order to avoid loading of lingu DLL + xSpell = new SpellDummy_Impl; + +/* if (!xLngSvcMgr.is()) + xLngSvcMgr = GetLngSvcMgr_Impl(); + + if (xLngSvcMgr.is()) + { + xSpell = uno::Reference< XSpellChecker1 > ( + xLngSvcMgr->getSpellChecker(), UNO_QUERY ); + } +*/ + return xSpell; +} + +uno::Reference< XHyphenator > LinguMgr::GetHyph() +{ + if (bExiting) + return 0; + + if (!pExitLstnr) + pExitLstnr = new LinguMgrExitLstnr; + + //! use dummy implementation in order to avoid loading of lingu DLL + xHyph = new HyphDummy_Impl; + +/* + if (!xLngSvcMgr.is()) + xLngSvcMgr = GetLngSvcMgr_Impl(); + + if (xLngSvcMgr.is()) + { + xHyph = xLngSvcMgr->getHyphenator(); + } +*/ + return xHyph; +} + +uno::Reference< XThesaurus > LinguMgr::GetThes() +{ + if (bExiting) + return 0; + + if (!pExitLstnr) + pExitLstnr = new LinguMgrExitLstnr; + + //! use dummy implementation in order to avoid loading of lingu DLL + //! when only the XSupportedLocales interface is used. + //! The dummy accesses the real implementation (and thus loading the DLL) + //! when "real" work needs to be done only. + xThes = new ThesDummy_Impl; +/* + if (!xLngSvcMgr.is()) + xLngSvcMgr = GetLngSvcMgr_Impl(); + + if (xLngSvcMgr.is()) + { + xThes = xLngSvcMgr->getThesaurus(); + } +*/ + return xThes; +} + + +void LinguMgr::UpdateAll() +{ +} + + +uno::Reference< XDictionaryList > LinguMgr::GetDicList() +{ + if (bExiting) + return 0; + + if (!pExitLstnr) + pExitLstnr = new LinguMgrExitLstnr; + + uno::Reference< XMultiServiceFactory > xMgr( getProcessServiceFactory() ); + if (xMgr.is()) + { + xDicList = uno::Reference< XDictionaryList > ( xMgr->createInstance( + A2OU("com.sun.star.linguistic2.DictionaryList") ), UNO_QUERY ); + } + return xDicList; +} + +uno::Reference< XPropertySet > LinguMgr::GetProp() +{ + if (bExiting) + return 0; + + if (!pExitLstnr) + pExitLstnr = new LinguMgrExitLstnr; + + uno::Reference< XMultiServiceFactory > xMgr( getProcessServiceFactory() ); + if (xMgr.is()) + { + xProp = uno::Reference< XPropertySet > ( xMgr->createInstance( + A2OU("com.sun.star.linguistic2.LinguProperties") ), UNO_QUERY ); + } + return xProp; +} + +uno::Reference< XDictionary > LinguMgr::GetIgnoreAll() +{ + if (bExiting) + return 0; + + if (!pExitLstnr) + pExitLstnr = new LinguMgrExitLstnr; + + uno::Reference< XDictionaryList > xTmpDicList( GetDictionaryList() ); + if (xTmpDicList.is()) + { + xIgnoreAll = uno::Reference< XDictionary > ( xTmpDicList->getDictionaryByName( + A2OU("IgnoreAllList") ), UNO_QUERY ); + } + return xIgnoreAll; +} + +uno::Reference< XDictionary > LinguMgr::GetChangeAll() +{ + if (bExiting) + return 0; + + if (!pExitLstnr) + pExitLstnr = new LinguMgrExitLstnr; + + uno::Reference< XDictionaryList > _xDicList( GetDictionaryList() , UNO_QUERY ); + if (_xDicList.is()) + { + xChangeAll = uno::Reference< XDictionary > ( + _xDicList->createDictionary( + A2OU("ChangeAllList"), + SvxCreateLocale( LANGUAGE_NONE ), + DictionaryType_NEGATIVE, String() ), UNO_QUERY ); + } + return xChangeAll; +} + +uno::Reference< XDictionary > LinguMgr::GetStandard() +{ + // Tries to return a dictionary which may hold positive entries is + // persistent and not read-only. + + if (bExiting) + return 0; + + uno::Reference< XDictionaryList > xTmpDicList( GetDictionaryList() ); + if (!xTmpDicList.is()) + return NULL; + + const OUString aDicName( RTL_CONSTASCII_USTRINGPARAM( "standard.dic" ) ); + uno::Reference< XDictionary > xDic( xTmpDicList->getDictionaryByName( aDicName ), + UNO_QUERY ); + if (!xDic.is()) + { + // try to create standard dictionary + uno::Reference< XDictionary > xTmp; + try + { + xTmp = xTmpDicList->createDictionary( aDicName, + SvxCreateLocale( LANGUAGE_NONE ), + DictionaryType_POSITIVE, + linguistic::GetWritableDictionaryURL( aDicName ) ); + } + catch(com::sun::star::uno::Exception &) + { + } + + // add new dictionary to list + if (xTmp.is()) + xTmpDicList->addDictionary( xTmp ); + xDic = uno::Reference< XDictionary > ( xTmp, UNO_QUERY ); + } +#if OSL_DEBUG_LEVEL > 1 + uno::Reference< XStorable > xStor( xDic, UNO_QUERY ); + DBG_ASSERT( xDic.is() && xDic->getDictionaryType() == DictionaryType_POSITIVE, + "wrong dictionary type"); + DBG_ASSERT( xDic.is() && SvxLocaleToLanguage( xDic->getLocale() ) == LANGUAGE_NONE, + "wrong dictionary language"); + DBG_ASSERT( !xStor.is() || (xStor->hasLocation() && !xStor->isReadonly()), + "dictionary not editable" ); +#endif + + return xDic; +} + +/////////////////////////////////////////////////////////////////////////// + +uno::Reference< XSpellChecker1 > SvxGetSpellChecker() +{ + return LinguMgr::GetSpellChecker(); +} + +uno::Reference< XHyphenator > SvxGetHyphenator() +{ + return LinguMgr::GetHyphenator(); +} + +uno::Reference< XThesaurus > SvxGetThesaurus() +{ + return LinguMgr::GetThesaurus(); +} + +uno::Reference< XDictionaryList > SvxGetDictionaryList() +{ + return LinguMgr::GetDictionaryList(); +} + +uno::Reference< XPropertySet > SvxGetLinguPropertySet() +{ + return LinguMgr::GetLinguPropertySet(); +} + +//TL:TODO: remove argument or provide SvxGetIgnoreAllList with the same one +uno::Reference< XDictionary > SvxGetOrCreatePosDic( + uno::Reference< XDictionaryList > /* xDicList */ ) +{ + return LinguMgr::GetStandardDic(); +} + +uno::Reference< XDictionary > SvxGetIgnoreAllList() +{ + return LinguMgr::GetIgnoreAllList(); +} + +uno::Reference< XDictionary > SvxGetChangeAllList() +{ + return LinguMgr::GetChangeAllList(); +} + +/////////////////////////////////////////////////////////////////////////// + + +#include <com/sun/star/linguistic2/XHyphenatedWord.hpp> + +SvxAlternativeSpelling SvxGetAltSpelling( + const ::com::sun::star::uno::Reference< + ::com::sun::star::linguistic2::XHyphenatedWord > & rHyphWord ) +{ + SvxAlternativeSpelling aRes; + if (rHyphWord.is() && rHyphWord->isAlternativeSpelling()) + { + OUString aWord( rHyphWord->getWord() ), + aAltWord( rHyphWord->getHyphenatedWord() ); + INT16 nHyphenationPos = rHyphWord->getHyphenationPos(), + nHyphenPos = rHyphWord->getHyphenPos(); + INT16 nLen = (INT16)aWord.getLength(); + INT16 nAltLen = (INT16)aAltWord.getLength(); + const sal_Unicode *pWord = aWord.getStr(), + *pAltWord = aAltWord.getStr(); + + // count number of chars from the left to the + // hyphenation pos / hyphen pos that are equal + INT16 nL = 0; + while (nL <= nHyphenationPos && nL <= nHyphenPos + && pWord[ nL ] == pAltWord[ nL ]) + ++nL; + // count number of chars from the right to the + // hyphenation pos / hyphen pos that are equal + INT16 nR = 0; + INT32 nIdx = nLen - 1; + INT32 nAltIdx = nAltLen - 1; + while (nIdx > nHyphenationPos && nAltIdx > nHyphenPos + && pWord[ nIdx-- ] == pAltWord[ nAltIdx-- ]) + ++nR; + + aRes.aReplacement = OUString( aAltWord.copy( nL, nAltLen - nL - nR ) ); + aRes.nChangedPos = (INT16) nL; + aRes.nChangedLength = nLen - nL - nR; + aRes.bIsAltSpelling = TRUE; + aRes.xHyphWord = rHyphWord; + } + return aRes; +} + + +/////////////////////////////////////////////////////////////////////////// + +SvxDicListChgClamp::SvxDicListChgClamp( uno::Reference< XDictionaryList > &rxDicList ) : + xDicList ( rxDicList ) +{ + if (xDicList.is()) + { + xDicList->beginCollectEvents(); + } +} + +SvxDicListChgClamp::~SvxDicListChgClamp() +{ + if (xDicList.is()) + { + xDicList->endCollectEvents(); + } +} + +/////////////////////////////////////////////////////////////////////////// + +short SvxDicError( Window *pParent, sal_Int16 nError ) +{ + short nRes = 0; + if (DIC_ERR_NONE != nError) + { + int nRid; + switch (nError) + { + case DIC_ERR_FULL : nRid = RID_SVXSTR_DIC_ERR_FULL; break; + case DIC_ERR_READONLY : nRid = RID_SVXSTR_DIC_ERR_READONLY; break; + default: + nRid = RID_SVXSTR_DIC_ERR_UNKNOWN; + DBG_ASSERT(0, "unexpected case"); + } + nRes = InfoBox( pParent, EE_RESSTR( nRid ) ).Execute(); + } + return nRes; +} + +LanguageType SvxLocaleToLanguage( const Locale& rLocale ) +{ + // empty Locale -> LANGUAGE_NONE + if ( rLocale.Language.getLength() == 0 ) + return LANGUAGE_NONE; + + return MsLangId::convertLocaleToLanguage( rLocale ); +} + +Locale& SvxLanguageToLocale( Locale& rLocale, LanguageType eLang ) +{ + if ( eLang != LANGUAGE_NONE /* && eLang != LANGUAGE_SYSTEM */) + MsLangId::convertLanguageToLocale( eLang, rLocale ); + else + rLocale = Locale(); + + return rLocale; +} + +Locale SvxCreateLocale( LanguageType eLang ) +{ + Locale aLocale; + if ( eLang != LANGUAGE_NONE /* && eLang != LANGUAGE_SYSTEM */) + MsLangId::convertLanguageToLocale( eLang, aLocale ); + + return aLocale; +} + + |