/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_unotools.hxx"


#include <com/sun/star/lang/Locale.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/lang/XSingleServiceFactory.hpp>
#include <com/sun/star/container/XNameAccess.hpp>
#include <com/sun/star/container/XNameContainer.hpp>
#include <com/sun/star/container/XNameReplace.hpp>
#include "com/sun/star/util/XMacroExpander.hpp"
#include "com/sun/star/beans/XPropertySet.hpp"
#include <rtl/uri.hxx>
#include <vos/mutex.hxx>
#include <i18npool/mslangid.hxx>
#include <tools/debug.hxx>
#include <tools/string.hxx>
#include <unotools/lingucfg.hxx>
#include <unotools/linguprops.hxx>

#include <comphelper/processfactory.hxx>

#include <itemholder1.hxx>

using namespace rtl;
using namespace com::sun::star;

#define A2OU(x)        ::rtl::OUString::createFromAscii( x )
#define EXPAND_PROTOCOL     "vnd.sun.star.expand:"
#define FILE_PROTOCOL       "file:///"

///////////////////////////////////////////////////////////////////////////


static osl::Mutex &  GetOwnMutex()
{
    static osl::Mutex aMutex;
    return aMutex;
}


///////////////////////////////////////////////////////////////////////////


static sal_Bool lcl_SetLocale( sal_Int16 &rLanguage, const uno::Any &rVal )
{
    sal_Bool bSucc = sal_False;

    lang::Locale aNew;
    if (rVal >>= aNew)	// conversion successful?
    {
        sal_Int16 nNew = MsLangId::convertLocaleToLanguage( aNew );
        if (nNew != rLanguage)
        {
            rLanguage = nNew;
            bSucc = sal_True;
        }
    }
    return bSucc;
}


static inline const OUString lcl_LanguageToCfgLocaleStr( sal_Int16 nLanguage )
{
    OUString aRes;
    if (LANGUAGE_SYSTEM != nLanguage)
        aRes = MsLangId::convertLanguageToIsoString( nLanguage );
    return aRes;
}


static sal_Int16 lcl_CfgAnyToLanguage( const uno::Any &rVal )
{
    OUString aTmp;
    rVal >>= aTmp;
    return (aTmp.getLength() == 0) ? LANGUAGE_SYSTEM : MsLangId::convertIsoStringToLanguage( aTmp );
}


//////////////////////////////////////////////////////////////////////

SvtLinguOptions::SvtLinguOptions()
{
    nDefaultLanguage = LANGUAGE_NONE;
	nDefaultLanguage_CJK = LANGUAGE_NONE;
	nDefaultLanguage_CTL = LANGUAGE_NONE;

	// general options
	bIsUseDictionaryList	=
	bIsIgnoreControlCharacters	= sal_True;

	// spelling options
	bIsSpellCapitalization	=
	bIsSpellSpecial			= sal_True;
	bIsSpellAuto			=
	bIsSpellReverse			=
	bIsSpellWithDigits		=
	bIsSpellUpperCase		= sal_False;

    // text conversion options
    bIsIgnorePostPositionalWord     = sal_True;
    bIsAutoCloseDialog              =
    bIsShowEntriesRecentlyUsedFirst =
    bIsAutoReplaceUniqueEntries     = sal_False;
    bIsDirectionToSimplified        = sal_True;
    bIsUseCharacterVariants         =
    bIsTranslateCommonTerms         =
    bIsReverseMapping               = sal_False;

    bROIsDirectionToSimplified      =
    bROIsUseCharacterVariants       =
    bROIsTranslateCommonTerms       =
    bROIsReverseMapping             = sal_False;

	// hyphenation options
	bIsHyphSpecial			= sal_True;
	bIsHyphAuto				= sal_False;
	nHyphMinLeading			=
	nHyphMinTrailing		= 2;
	nHyphMinWordLength		= 0;

    nDataFilesChangedCheckValue = 0;

    //grammar options
    bIsGrammarAuto = sal_False,
    bIsGrammarInteractive = sal_False;

}


//////////////////////////////////////////////////////////////////////


class SvtLinguConfigItem : public utl::ConfigItem
{
    SvtLinguOptions     aOpt;

    // disallow copy-constructor and assignment-operator for now
    SvtLinguConfigItem( const SvtLinguConfigItem & );
    SvtLinguConfigItem & operator = ( const SvtLinguConfigItem & );

    static sal_Bool GetHdlByName( sal_Int32 &rnHdl, const OUString &rPropertyName, sal_Bool bFullPropName = sal_False );
    static const uno::Sequence< OUString > & GetPropertyNames();
    sal_Bool                LoadOptions( const uno::Sequence< OUString > &rProperyNames );
    sal_Bool                SaveOptions( const uno::Sequence< OUString > &rProperyNames );

public:
    SvtLinguConfigItem();
    virtual ~SvtLinguConfigItem();

    // utl::ConfigItem
    virtual void    Notify( const com::sun::star::uno::Sequence< rtl::OUString > &rPropertyNames );
    virtual void    Commit();

    // make some protected functions of utl::ConfigItem public
    using utl::ConfigItem::GetNodeNames;
    using utl::ConfigItem::GetProperties;
    //using utl::ConfigItem::PutProperties;
    //using utl::ConfigItem::SetSetProperties;
    using utl::ConfigItem::ReplaceSetProperties;
    //using utl::ConfigItem::GetReadOnlyStates;


    com::sun::star::uno::Any
            GetProperty( const rtl::OUString &rPropertyName ) const;
    com::sun::star::uno::Any
            GetProperty( sal_Int32 nPropertyHandle ) const;

    sal_Bool    SetProperty( const rtl::OUString &rPropertyName,
                         const com::sun::star::uno::Any &rValue );
    sal_Bool    SetProperty( sal_Int32 nPropertyHandle,
                         const com::sun::star::uno::Any &rValue );

    sal_Bool    GetOptions( SvtLinguOptions &rOptions ) const;
    sal_Bool    SetOptions( const SvtLinguOptions &rOptions );

    sal_Bool    IsReadOnly( const rtl::OUString &rPropertyName ) const;
    sal_Bool    IsReadOnly( sal_Int32 nPropertyHandle ) const;
};


SvtLinguConfigItem::SvtLinguConfigItem() :
    utl::ConfigItem( String::CreateFromAscii( "Office.Linguistic" ) )
{
    LoadOptions( GetPropertyNames() );
    ClearModified();

    // request notify events when properties change
    EnableNotification( GetPropertyNames() );
}


SvtLinguConfigItem::~SvtLinguConfigItem()
{
    //! Commit (SaveOptions) will be called by the d-tor of the base called !
}


void SvtLinguConfigItem::Notify( const uno::Sequence< OUString > &rPropertyNames )
{
    LoadOptions( rPropertyNames );
	NotifyListeners(0);
}


void SvtLinguConfigItem::Commit()
{
    SaveOptions( GetPropertyNames() );
}


static struct NamesToHdl
{
    const char   *pFullPropName;      // full qualified name as used in configuration
    const char   *pPropName;          // property name only (atom) of above
    sal_Int32   nHdl;               // numeric handle representing the property
}aNamesToHdl[] =
{
{/*  0 */    "General/DefaultLocale",                         UPN_DEFAULT_LOCALE,                    UPH_DEFAULT_LOCALE},
{/*  1 */    "General/DictionaryList/ActiveDictionaries",     UPN_ACTIVE_DICTIONARIES,               UPH_ACTIVE_DICTIONARIES},
{/*  2 */    "General/DictionaryList/IsUseDictionaryList",    UPN_IS_USE_DICTIONARY_LIST,            UPH_IS_USE_DICTIONARY_LIST},
{/*  3 */    "General/IsIgnoreControlCharacters",             UPN_IS_IGNORE_CONTROL_CHARACTERS,      UPH_IS_IGNORE_CONTROL_CHARACTERS},
{/*  5 */    "General/DefaultLocale_CJK",                     UPN_DEFAULT_LOCALE_CJK,                UPH_DEFAULT_LOCALE_CJK},
{/*  6 */    "General/DefaultLocale_CTL",                     UPN_DEFAULT_LOCALE_CTL,                UPH_DEFAULT_LOCALE_CTL},

{/*  7 */    "SpellChecking/IsSpellUpperCase",                UPN_IS_SPELL_UPPER_CASE,               UPH_IS_SPELL_UPPER_CASE},
{/*  8 */    "SpellChecking/IsSpellWithDigits",               UPN_IS_SPELL_WITH_DIGITS,              UPH_IS_SPELL_WITH_DIGITS},
{/*  9 */    "SpellChecking/IsSpellCapitalization",           UPN_IS_SPELL_CAPITALIZATION,           UPH_IS_SPELL_CAPITALIZATION},
{/* 10 */    "SpellChecking/IsSpellAuto",                     UPN_IS_SPELL_AUTO,                     UPH_IS_SPELL_AUTO},
{/* 11 */    "SpellChecking/IsSpellSpecial",                  UPN_IS_SPELL_SPECIAL,                  UPH_IS_SPELL_SPECIAL},
{/* 14 */    "SpellChecking/IsReverseDirection",              UPN_IS_WRAP_REVERSE,                   UPH_IS_WRAP_REVERSE},

{/* 15 */    "Hyphenation/MinLeading",                        UPN_HYPH_MIN_LEADING,                  UPH_HYPH_MIN_LEADING},
{/* 16 */    "Hyphenation/MinTrailing",                       UPN_HYPH_MIN_TRAILING,                 UPH_HYPH_MIN_TRAILING},
{/* 17 */    "Hyphenation/MinWordLength",                     UPN_HYPH_MIN_WORD_LENGTH,              UPH_HYPH_MIN_WORD_LENGTH},
{/* 18 */    "Hyphenation/IsHyphSpecial",                     UPN_IS_HYPH_SPECIAL,                   UPH_IS_HYPH_SPECIAL},
{/* 19 */    "Hyphenation/IsHyphAuto",                        UPN_IS_HYPH_AUTO,                      UPH_IS_HYPH_AUTO},

{/* 20 */    "TextConversion/ActiveConversionDictionaries",   UPN_ACTIVE_CONVERSION_DICTIONARIES,        UPH_ACTIVE_CONVERSION_DICTIONARIES},
{/* 21 */    "TextConversion/IsIgnorePostPositionalWord",     UPN_IS_IGNORE_POST_POSITIONAL_WORD,        UPH_IS_IGNORE_POST_POSITIONAL_WORD},
{/* 22 */    "TextConversion/IsAutoCloseDialog",              UPN_IS_AUTO_CLOSE_DIALOG,                  UPH_IS_AUTO_CLOSE_DIALOG},
{/* 23 */    "TextConversion/IsShowEntriesRecentlyUsedFirst", UPN_IS_SHOW_ENTRIES_RECENTLY_USED_FIRST,   UPH_IS_SHOW_ENTRIES_RECENTLY_USED_FIRST},
{/* 24 */    "TextConversion/IsAutoReplaceUniqueEntries",     UPN_IS_AUTO_REPLACE_UNIQUE_ENTRIES,        UPH_IS_AUTO_REPLACE_UNIQUE_ENTRIES},
{/* 25 */    "TextConversion/IsDirectionToSimplified",        UPN_IS_DIRECTION_TO_SIMPLIFIED,            UPH_IS_DIRECTION_TO_SIMPLIFIED},
{/* 26 */    "TextConversion/IsUseCharacterVariants",         UPN_IS_USE_CHARACTER_VARIANTS,             UPH_IS_USE_CHARACTER_VARIANTS},
{/* 27 */    "TextConversion/IsTranslateCommonTerms",         UPN_IS_TRANSLATE_COMMON_TERMS,             UPH_IS_TRANSLATE_COMMON_TERMS},
{/* 28 */    "TextConversion/IsReverseMapping",               UPN_IS_REVERSE_MAPPING,                    UPH_IS_REVERSE_MAPPING},

{/* 29 */    "ServiceManager/DataFilesChangedCheckValue",     UPN_DATA_FILES_CHANGED_CHECK_VALUE,        UPH_DATA_FILES_CHANGED_CHECK_VALUE},

{/* 30 */    "GrammarChecking/IsAutoCheck",                   UPN_IS_GRAMMAR_AUTO,                      UPH_IS_GRAMMAR_AUTO},
{/* 31 */    "GrammarChecking/IsInteractiveCheck",            UPN_IS_GRAMMAR_INTERACTIVE,               UPH_IS_GRAMMAR_INTERACTIVE},

            /* similar to entry 0 (thus no own configuration entry) but with different property name and type */
{            NULL,											 UPN_DEFAULT_LANGUAGE,                      UPH_DEFAULT_LANGUAGE},

{            NULL,                                            NULL,                                      -1}
};


const uno::Sequence< OUString > & SvtLinguConfigItem::GetPropertyNames()
{
    static uno::Sequence< OUString > aNames;
	static sal_Bool bInitialized = sal_False;

	if (!bInitialized)
	{
		sal_Int32 nMax = sizeof(aNamesToHdl) / sizeof(aNamesToHdl[0]);

		aNames.realloc( nMax );
		OUString *pNames = aNames.getArray();
		sal_Int32 nIdx = 0;
		for (sal_Int32 i = 0; i < nMax;  ++i)
		{
			const sal_Char *pFullPropName = aNamesToHdl[i].pFullPropName;
			if (pFullPropName)
				pNames[ nIdx++ ] = A2OU( pFullPropName );
		}
		aNames.realloc( nIdx );
		bInitialized = sal_True;
	}
	return aNames;
}


sal_Bool SvtLinguConfigItem::GetHdlByName(
	sal_Int32 &rnHdl,
	const OUString &rPropertyName,
	sal_Bool bFullPropName )
{
    NamesToHdl *pEntry = &aNamesToHdl[0];

	if (bFullPropName)
	{
		while (pEntry && pEntry->pFullPropName != NULL)
		{
			if (0 == rPropertyName.compareToAscii( pEntry->pFullPropName ))
			{
				rnHdl = pEntry->nHdl;
				break;
			}
			++pEntry;
		}
		return pEntry && pEntry->pFullPropName != NULL;
	}
	else
	{
		while (pEntry && pEntry->pPropName != NULL)
		{
			if (0 == rPropertyName.compareToAscii( pEntry->pPropName ))
			{
				rnHdl = pEntry->nHdl;
				break;
			}
			++pEntry;
		}
		return pEntry && pEntry->pPropName != NULL;
	}
}


uno::Any SvtLinguConfigItem::GetProperty( const OUString &rPropertyName ) const
{
    osl::MutexGuard aGuard( GetOwnMutex() );

    sal_Int32 nHdl;
    return GetHdlByName( nHdl, rPropertyName ) ? GetProperty( nHdl ) : uno::Any();
}


uno::Any SvtLinguConfigItem::GetProperty( sal_Int32 nPropertyHandle ) const
{
    osl::MutexGuard aGuard( GetOwnMutex() );

    uno::Any aRes;

    const sal_Int16 *pnVal = 0;
    const sal_Bool  *pbVal = 0;
    const sal_Int32 *pnInt32Val = 0;

    const SvtLinguOptions &rOpt = const_cast< SvtLinguConfigItem * >(this)->aOpt;
    switch (nPropertyHandle)
    {
        case UPH_IS_USE_DICTIONARY_LIST :   pbVal = &rOpt.bIsUseDictionaryList; break;
        case UPH_IS_IGNORE_CONTROL_CHARACTERS : pbVal = &rOpt.bIsIgnoreControlCharacters;   break;
        case UPH_IS_HYPH_AUTO :             pbVal = &rOpt.bIsHyphAuto;  break;
        case UPH_IS_HYPH_SPECIAL :          pbVal = &rOpt.bIsHyphSpecial;   break;
        case UPH_IS_SPELL_AUTO :            pbVal = &rOpt.bIsSpellAuto; break;
        case UPH_IS_SPELL_SPECIAL :         pbVal = &rOpt.bIsSpellSpecial;  break;
        case UPH_IS_WRAP_REVERSE :          pbVal = &rOpt.bIsSpellReverse;  break;
        case UPH_DEFAULT_LANGUAGE :         pnVal = &rOpt.nDefaultLanguage; break;
        case UPH_IS_SPELL_CAPITALIZATION :  pbVal = &rOpt.bIsSpellCapitalization;       break;
        case UPH_IS_SPELL_WITH_DIGITS :     pbVal = &rOpt.bIsSpellWithDigits;   break;
        case UPH_IS_SPELL_UPPER_CASE :      pbVal = &rOpt.bIsSpellUpperCase;        break;
        case UPH_HYPH_MIN_LEADING :         pnVal = &rOpt.nHyphMinLeading;      break;
        case UPH_HYPH_MIN_TRAILING :        pnVal = &rOpt.nHyphMinTrailing; break;
        case UPH_HYPH_MIN_WORD_LENGTH :     pnVal = &rOpt.nHyphMinWordLength;   break;
        case UPH_ACTIVE_DICTIONARIES :
        {
            aRes <<= rOpt.aActiveDics;
            break;
        }
        case UPH_ACTIVE_CONVERSION_DICTIONARIES :
        {
            aRes <<= rOpt.aActiveConvDics;
            break;
        }
        case UPH_DEFAULT_LOCALE :
        {
            lang::Locale aLocale( MsLangId::convertLanguageToLocale( rOpt.nDefaultLanguage, false ) );
            aRes.setValue( &aLocale, ::getCppuType((lang::Locale*)0 ));
            break;
        }
        case UPH_DEFAULT_LOCALE_CJK :
        {
            lang::Locale aLocale( MsLangId::convertLanguageToLocale( rOpt.nDefaultLanguage_CJK, false ) );
            aRes.setValue( &aLocale, ::getCppuType((lang::Locale*)0 ));
            break;
        }
        case UPH_DEFAULT_LOCALE_CTL :
        {
            lang::Locale aLocale( MsLangId::convertLanguageToLocale( rOpt.nDefaultLanguage_CTL, false ) );
            aRes.setValue( &aLocale, ::getCppuType((lang::Locale*)0 ));
            break;
        }
        case UPH_IS_IGNORE_POST_POSITIONAL_WORD :       pbVal = &rOpt.bIsIgnorePostPositionalWord; break;
        case UPH_IS_AUTO_CLOSE_DIALOG :                 pbVal = &rOpt.bIsAutoCloseDialog; break;
        case UPH_IS_SHOW_ENTRIES_RECENTLY_USED_FIRST :  pbVal = &rOpt.bIsShowEntriesRecentlyUsedFirst; break;
        case UPH_IS_AUTO_REPLACE_UNIQUE_ENTRIES :       pbVal = &rOpt.bIsAutoReplaceUniqueEntries; break;

        case UPH_IS_DIRECTION_TO_SIMPLIFIED:            pbVal = &rOpt.bIsDirectionToSimplified; break;
        case UPH_IS_USE_CHARACTER_VARIANTS :            pbVal = &rOpt.bIsUseCharacterVariants; break;
        case UPH_IS_TRANSLATE_COMMON_TERMS :            pbVal = &rOpt.bIsTranslateCommonTerms; break;
        case UPH_IS_REVERSE_MAPPING :                   pbVal = &rOpt.bIsReverseMapping; break;

        case UPH_DATA_FILES_CHANGED_CHECK_VALUE :       pnInt32Val = &rOpt.nDataFilesChangedCheckValue; break;
        case UPH_IS_GRAMMAR_AUTO:                       pbVal = &rOpt.bIsGrammarAuto; break;
        case UPH_IS_GRAMMAR_INTERACTIVE:                pbVal = &rOpt.bIsGrammarInteractive; break;
        default :
            DBG_ASSERT( 0, "unexpected property handle" );
    }

    if (pbVal)
        aRes <<= *pbVal;
    else if (pnVal)
        aRes <<= *pnVal;
    else if (pnInt32Val)
        aRes <<= *pnInt32Val;

    return aRes;
}


sal_Bool SvtLinguConfigItem::SetProperty( const OUString &rPropertyName, const uno::Any &rValue )
{
    osl::MutexGuard aGuard( GetOwnMutex() );

    sal_Bool bSucc = sal_False;
    sal_Int32 nHdl;
    if (GetHdlByName( nHdl, rPropertyName ))
        bSucc = SetProperty( nHdl, rValue );
    return bSucc;
}


sal_Bool SvtLinguConfigItem::SetProperty( sal_Int32 nPropertyHandle, const uno::Any &rValue )
{
    osl::MutexGuard aGuard( GetOwnMutex() );

    sal_Bool bSucc = sal_False;
    if (!rValue.hasValue())
        return bSucc;

    sal_Bool bMod = sal_False;

    sal_Int16 *pnVal = 0;
    sal_Bool  *pbVal = 0;
    sal_Int32 *pnInt32Val = 0;

    SvtLinguOptions &rOpt = aOpt;
    switch (nPropertyHandle)
    {
        case UPH_IS_USE_DICTIONARY_LIST :   pbVal = &rOpt.bIsUseDictionaryList;    break;
        case UPH_IS_IGNORE_CONTROL_CHARACTERS : pbVal = &rOpt.bIsIgnoreControlCharacters;  break;
        case UPH_IS_HYPH_AUTO :             pbVal = &rOpt.bIsHyphAuto; break;
        case UPH_IS_HYPH_SPECIAL :          pbVal = &rOpt.bIsHyphSpecial;  break;
        case UPH_IS_SPELL_AUTO :            pbVal = &rOpt.bIsSpellAuto;    break;
        case UPH_IS_SPELL_SPECIAL :         pbVal = &rOpt.bIsSpellSpecial; break;
        case UPH_IS_WRAP_REVERSE :          pbVal = &rOpt.bIsSpellReverse; break;
        case UPH_DEFAULT_LANGUAGE :         pnVal = &rOpt.nDefaultLanguage;    break;
        case UPH_IS_SPELL_CAPITALIZATION :  pbVal = &rOpt.bIsSpellCapitalization;      break;
        case UPH_IS_SPELL_WITH_DIGITS :     pbVal = &rOpt.bIsSpellWithDigits;  break;
        case UPH_IS_SPELL_UPPER_CASE :      pbVal = &rOpt.bIsSpellUpperCase;       break;
        case UPH_HYPH_MIN_LEADING :         pnVal = &rOpt.nHyphMinLeading;     break;
        case UPH_HYPH_MIN_TRAILING :        pnVal = &rOpt.nHyphMinTrailing;    break;
        case UPH_HYPH_MIN_WORD_LENGTH :     pnVal = &rOpt.nHyphMinWordLength;  break;
        case UPH_ACTIVE_DICTIONARIES :
        {
            rValue >>= rOpt.aActiveDics;
            bMod = sal_True;
            break;
        }
        case UPH_ACTIVE_CONVERSION_DICTIONARIES :
        {
            rValue >>= rOpt.aActiveConvDics;
            bMod = sal_True;
            break;
        }
        case UPH_DEFAULT_LOCALE :
        {
            bSucc = lcl_SetLocale( rOpt.nDefaultLanguage, rValue );
            bMod = bSucc;
            break;
        }
        case UPH_DEFAULT_LOCALE_CJK :
        {
            bSucc = lcl_SetLocale( rOpt.nDefaultLanguage_CJK, rValue );
            bMod = bSucc;
            break;
        }
        case UPH_DEFAULT_LOCALE_CTL :
        {
            bSucc = lcl_SetLocale( rOpt.nDefaultLanguage_CTL, rValue );
            bMod = bSucc;
            break;
        }
        case UPH_IS_IGNORE_POST_POSITIONAL_WORD :       pbVal = &rOpt.bIsIgnorePostPositionalWord; break;
        case UPH_IS_AUTO_CLOSE_DIALOG :                 pbVal = &rOpt.bIsAutoCloseDialog; break;
        case UPH_IS_SHOW_ENTRIES_RECENTLY_USED_FIRST :  pbVal = &rOpt.bIsShowEntriesRecentlyUsedFirst; break;
        case UPH_IS_AUTO_REPLACE_UNIQUE_ENTRIES :       pbVal = &rOpt.bIsAutoReplaceUniqueEntries; break;

        case UPH_IS_DIRECTION_TO_SIMPLIFIED :           pbVal = &rOpt.bIsDirectionToSimplified; break;
        case UPH_IS_USE_CHARACTER_VARIANTS :            pbVal = &rOpt.bIsUseCharacterVariants; break;
        case UPH_IS_TRANSLATE_COMMON_TERMS :            pbVal = &rOpt.bIsTranslateCommonTerms; break;
        case UPH_IS_REVERSE_MAPPING :                   pbVal = &rOpt.bIsReverseMapping; break;

        case UPH_DATA_FILES_CHANGED_CHECK_VALUE :       pnInt32Val = &rOpt.nDataFilesChangedCheckValue; break;
        case UPH_IS_GRAMMAR_AUTO:                       pbVal = &rOpt.bIsGrammarAuto; break;
        case UPH_IS_GRAMMAR_INTERACTIVE:                pbVal = &rOpt.bIsGrammarInteractive; break;
        default :
            DBG_ASSERT( 0, "unexpected property handle" );
    }

    if (pbVal)
    {
        sal_Bool bNew = sal_Bool();
        if (rValue >>= bNew)
        {
            if (bNew != *pbVal)
            {
                *pbVal = bNew;
                bMod = sal_True;
            }
            bSucc = sal_True;
        }
    }
    else if (pnVal)
    {
        sal_Int16 nNew = sal_Int16();
        if (rValue >>= nNew)
        {
            if (nNew != *pnVal)
            {
                *pnVal = nNew;
                bMod = sal_True;
            }
            bSucc = sal_True;
        }
    }
    else if (pnInt32Val)
    {
        sal_Int32 nNew = sal_Int32();
        if (rValue >>= nNew)
        {
            if (nNew != *pnInt32Val)
            {
                *pnInt32Val = nNew;
                bMod = sal_True;
            }
            bSucc = sal_True;
        }
    }

    if (bMod)
        SetModified();

	NotifyListeners(0);
    return bSucc;
}


sal_Bool SvtLinguConfigItem::GetOptions( SvtLinguOptions &rOptions ) const
{
    osl::MutexGuard aGuard( GetOwnMutex() );

    rOptions = aOpt;
    return sal_True;
}


sal_Bool SvtLinguConfigItem::SetOptions( const SvtLinguOptions &rOptions )
{
    osl::MutexGuard aGuard( GetOwnMutex() );

    aOpt = rOptions;
    SetModified();
	NotifyListeners(0);
    return sal_True;
}


sal_Bool SvtLinguConfigItem::LoadOptions( const uno::Sequence< OUString > &rProperyNames )
{
    osl::MutexGuard aGuard( GetOwnMutex() );

    sal_Bool bRes = sal_False;

    const OUString *pProperyNames = rProperyNames.getConstArray();
    sal_Int32 nProps = rProperyNames.getLength();

    const uno::Sequence< uno::Any > aValues = GetProperties( rProperyNames );
    const uno::Sequence< sal_Bool > aROStates = GetReadOnlyStates( rProperyNames );

    if (nProps  &&  aValues.getLength() == nProps &&  aROStates.getLength() == nProps)
    {
        SvtLinguOptions &rOpt = aOpt;

        const uno::Any *pValue = aValues.getConstArray();
        const sal_Bool *pROStates = aROStates.getConstArray();
        for (sal_Int32 i = 0;  i < nProps;  ++i)
        {
            const uno::Any &rVal = pValue[i];
            sal_Int32 nPropertyHandle;
            GetHdlByName( nPropertyHandle, pProperyNames[i], sal_True );
            switch ( nPropertyHandle )
            {
                case UPH_DEFAULT_LOCALE :
                    { rOpt.bRODefaultLanguage = pROStates[i]; rOpt.nDefaultLanguage = lcl_CfgAnyToLanguage( rVal ); } break;
                case UPH_ACTIVE_DICTIONARIES :
                    { rOpt.bROActiveDics = pROStates[i]; rVal >>= rOpt.aActiveDics;   } break;
                case UPH_IS_USE_DICTIONARY_LIST :
                    { rOpt.bROIsUseDictionaryList = pROStates[i]; rVal >>= rOpt.bIsUseDictionaryList;  } break;
                case UPH_IS_IGNORE_CONTROL_CHARACTERS :
                    { rOpt.bROIsIgnoreControlCharacters = pROStates[i]; rVal >>= rOpt.bIsIgnoreControlCharacters;    } break;
                case UPH_DEFAULT_LOCALE_CJK :
                    { rOpt.bRODefaultLanguage_CJK = pROStates[i]; rOpt.nDefaultLanguage_CJK = lcl_CfgAnyToLanguage( rVal );    } break;
                case UPH_DEFAULT_LOCALE_CTL :
                    { rOpt.bRODefaultLanguage_CTL = pROStates[i]; rOpt.nDefaultLanguage_CTL = lcl_CfgAnyToLanguage( rVal );    } break;

                case UPH_IS_SPELL_UPPER_CASE :
                    { rOpt.bROIsSpellUpperCase = pROStates[i]; rVal >>= rOpt.bIsSpellUpperCase; } break;
                case UPH_IS_SPELL_WITH_DIGITS :
                    { rOpt.bROIsSpellWithDigits = pROStates[i]; rVal >>= rOpt.bIsSpellWithDigits;    } break;
                case UPH_IS_SPELL_CAPITALIZATION :
                    { rOpt.bROIsSpellCapitalization = pROStates[i]; rVal >>= rOpt.bIsSpellCapitalization;    } break;
                case UPH_IS_SPELL_AUTO :
                    { rOpt.bROIsSpellAuto = pROStates[i]; rVal >>= rOpt.bIsSpellAuto;  } break;
                case UPH_IS_SPELL_SPECIAL :
                    { rOpt.bROIsSpellSpecial = pROStates[i]; rVal >>= rOpt.bIsSpellSpecial;   } break;
                case UPH_IS_WRAP_REVERSE :
                    { rOpt.bROIsSpellReverse = pROStates[i]; rVal >>= rOpt.bIsSpellReverse;   } break;

                case UPH_HYPH_MIN_LEADING :
                    { rOpt.bROHyphMinLeading = pROStates[i]; rVal >>= rOpt.nHyphMinLeading;   } break;
                case UPH_HYPH_MIN_TRAILING :
                    { rOpt.bROHyphMinTrailing = pROStates[i]; rVal >>= rOpt.nHyphMinTrailing;  } break;
                case UPH_HYPH_MIN_WORD_LENGTH :
                    { rOpt.bROHyphMinWordLength = pROStates[i]; rVal >>= rOpt.nHyphMinWordLength;    } break;
                case UPH_IS_HYPH_SPECIAL :
                    { rOpt.bROIsHyphSpecial = pROStates[i]; rVal >>= rOpt.bIsHyphSpecial;    } break;
                case UPH_IS_HYPH_AUTO :
                    { rOpt.bROIsHyphAuto = pROStates[i]; rVal >>= rOpt.bIsHyphAuto;   } break;

                case UPH_ACTIVE_CONVERSION_DICTIONARIES : { rOpt.bROActiveConvDics = pROStates[i]; rVal >>= rOpt.aActiveConvDics;   } break;

                case UPH_IS_IGNORE_POST_POSITIONAL_WORD :
                    { rOpt.bROIsIgnorePostPositionalWord = pROStates[i]; rVal >>= rOpt.bIsIgnorePostPositionalWord;  } break;
                case UPH_IS_AUTO_CLOSE_DIALOG :
                    { rOpt.bROIsAutoCloseDialog = pROStates[i]; rVal >>= rOpt.bIsAutoCloseDialog;  } break;
                case UPH_IS_SHOW_ENTRIES_RECENTLY_USED_FIRST :
                    { rOpt.bROIsShowEntriesRecentlyUsedFirst = pROStates[i]; rVal >>= rOpt.bIsShowEntriesRecentlyUsedFirst;  } break;
                case UPH_IS_AUTO_REPLACE_UNIQUE_ENTRIES :
                    { rOpt.bROIsAutoReplaceUniqueEntries = pROStates[i]; rVal >>= rOpt.bIsAutoReplaceUniqueEntries;  } break;

                case UPH_IS_DIRECTION_TO_SIMPLIFIED :
                    { rOpt.bROIsDirectionToSimplified = pROStates[i];
                            if( ! (rVal >>= rOpt.bIsDirectionToSimplified) )
                            {
                                //default is locale dependent:
                                if(  rOpt.nDefaultLanguage_CJK == LANGUAGE_CHINESE_HONGKONG
                                  || rOpt.nDefaultLanguage_CJK == LANGUAGE_CHINESE_MACAU
                                  || rOpt.nDefaultLanguage_CJK == LANGUAGE_CHINESE_TRADITIONAL )
                                {
                                    rOpt.bIsDirectionToSimplified = sal_False;
                                }
                                else
                                {
                                    rOpt.bIsDirectionToSimplified = sal_True;
                                }
                            }
                    } break;
                case UPH_IS_USE_CHARACTER_VARIANTS :
                    { rOpt.bROIsUseCharacterVariants = pROStates[i]; rVal >>= rOpt.bIsUseCharacterVariants;  } break;
                case UPH_IS_TRANSLATE_COMMON_TERMS :
                    { rOpt.bROIsTranslateCommonTerms = pROStates[i]; rVal >>= rOpt.bIsTranslateCommonTerms;  } break;
                case UPH_IS_REVERSE_MAPPING :
                    { rOpt.bROIsReverseMapping = pROStates[i]; rVal >>= rOpt.bIsReverseMapping;  } break;

                case UPH_DATA_FILES_CHANGED_CHECK_VALUE :
                    { rOpt.bRODataFilesChangedCheckValue = pROStates[i]; rVal >>= rOpt.nDataFilesChangedCheckValue;  } break;

                case UPH_IS_GRAMMAR_AUTO:
                    { rOpt.bROIsGrammarAuto = pROStates[i]; rVal >>= rOpt.bIsGrammarAuto; }
                break;
                case UPH_IS_GRAMMAR_INTERACTIVE:
                    { rOpt.bROIsGrammarInteractive = pROStates[i]; rVal >>= rOpt.bIsGrammarInteractive; }
                break;

                default:
                    DBG_ASSERT( 0, "unexpected case" );
            }
        }

        bRes = sal_True;
    }
    DBG_ASSERT( bRes, "LoadOptions failed" );

    return bRes;
}


sal_Bool SvtLinguConfigItem::SaveOptions( const uno::Sequence< OUString > &rProperyNames )
{
    if (!IsModified())
        return sal_True;

    osl::MutexGuard aGuard( GetOwnMutex() );

    sal_Bool bRet = sal_False;
    const uno::Type &rBOOL     = ::getBooleanCppuType();
    const uno::Type &rINT16    = ::getCppuType( (sal_Int16 *) NULL );
    const uno::Type &rINT32    = ::getCppuType( (sal_Int32 *) NULL );

    sal_Int32 nProps = rProperyNames.getLength();
    uno::Sequence< uno::Any > aValues( nProps );
    uno::Any *pValue = aValues.getArray();

    if (nProps  &&  aValues.getLength() == nProps)
    {
        const SvtLinguOptions &rOpt = aOpt;

        OUString aTmp( lcl_LanguageToCfgLocaleStr( rOpt.nDefaultLanguage ) );
        *pValue++ = uno::makeAny( aTmp );                               //   0
        *pValue++ = uno::makeAny( rOpt.aActiveDics );                   //   1
        pValue++->setValue( &rOpt.bIsUseDictionaryList, rBOOL );        //   2
        pValue++->setValue( &rOpt.bIsIgnoreControlCharacters, rBOOL );  //   3
        aTmp = lcl_LanguageToCfgLocaleStr( rOpt.nDefaultLanguage_CJK );
        *pValue++ = uno::makeAny( aTmp );                               //   5
        aTmp = lcl_LanguageToCfgLocaleStr( rOpt.nDefaultLanguage_CTL );
        *pValue++ = uno::makeAny( aTmp );                               //   6

        pValue++->setValue( &rOpt.bIsSpellUpperCase, rBOOL );          //   7
        pValue++->setValue( &rOpt.bIsSpellWithDigits, rBOOL );         //   8
        pValue++->setValue( &rOpt.bIsSpellCapitalization, rBOOL );     //   9
        pValue++->setValue( &rOpt.bIsSpellAuto, rBOOL );               //  10
        pValue++->setValue( &rOpt.bIsSpellSpecial, rBOOL );            //  11
        pValue++->setValue( &rOpt.bIsSpellReverse, rBOOL );            //  14

        pValue++->setValue( &rOpt.nHyphMinLeading, rINT16 );           //  15
        pValue++->setValue( &rOpt.nHyphMinTrailing, rINT16 );          //  16
        pValue++->setValue( &rOpt.nHyphMinWordLength, rINT16 );        //  17
        pValue++->setValue( &rOpt.bIsHyphSpecial, rBOOL );             //  18
        pValue++->setValue( &rOpt.bIsHyphAuto, rBOOL );                //  19

        *pValue++ = uno::makeAny( rOpt.aActiveConvDics );               //   20

        pValue++->setValue( &rOpt.bIsIgnorePostPositionalWord, rBOOL ); //  21
        pValue++->setValue( &rOpt.bIsAutoCloseDialog, rBOOL );          //  22
        pValue++->setValue( &rOpt.bIsShowEntriesRecentlyUsedFirst, rBOOL ); //  23
        pValue++->setValue( &rOpt.bIsAutoReplaceUniqueEntries, rBOOL ); //  24

        pValue++->setValue( &rOpt.bIsDirectionToSimplified, rBOOL ); //  25
        pValue++->setValue( &rOpt.bIsUseCharacterVariants, rBOOL ); //  26
        pValue++->setValue( &rOpt.bIsTranslateCommonTerms, rBOOL ); //  27
        pValue++->setValue( &rOpt.bIsReverseMapping, rBOOL ); //  28

        pValue++->setValue( &rOpt.nDataFilesChangedCheckValue, rINT32 ); //  29
        pValue++->setValue( &rOpt.bIsGrammarAuto, rBOOL ); //  30
        pValue++->setValue( &rOpt.bIsGrammarInteractive, rBOOL ); // 31

        bRet |= PutProperties( rProperyNames, aValues );
    }

    if (bRet)
        ClearModified();

    return bRet;
}

sal_Bool SvtLinguConfigItem::IsReadOnly( const rtl::OUString &rPropertyName ) const
{
    osl::MutexGuard aGuard( GetOwnMutex() );

    sal_Bool bReadOnly = sal_False;
    sal_Int32 nHdl;
    if (GetHdlByName( nHdl, rPropertyName ))
        bReadOnly = IsReadOnly( nHdl );
    return bReadOnly;
}

sal_Bool SvtLinguConfigItem::IsReadOnly( sal_Int32 nPropertyHandle ) const
{
    osl::MutexGuard aGuard( GetOwnMutex() );

    sal_Bool bReadOnly = sal_False;

    const SvtLinguOptions &rOpt = const_cast< SvtLinguConfigItem * >(this)->aOpt;
    switch(nPropertyHandle)
    {
        case UPH_IS_USE_DICTIONARY_LIST         : bReadOnly = rOpt.bROIsUseDictionaryList      ; break;
        case UPH_IS_IGNORE_CONTROL_CHARACTERS   : bReadOnly = rOpt.bROIsIgnoreControlCharacters; break;
        case UPH_IS_HYPH_AUTO                   : bReadOnly = rOpt.bROIsHyphAuto               ; break;
        case UPH_IS_HYPH_SPECIAL                : bReadOnly = rOpt.bROIsHyphSpecial            ; break;
        case UPH_IS_SPELL_AUTO                  : bReadOnly = rOpt.bROIsSpellAuto              ; break;
        case UPH_IS_SPELL_SPECIAL               : bReadOnly = rOpt.bROIsSpellSpecial           ; break;
        case UPH_IS_WRAP_REVERSE                : bReadOnly = rOpt.bROIsSpellReverse           ; break;
        case UPH_DEFAULT_LANGUAGE               : bReadOnly = rOpt.bRODefaultLanguage          ; break;
        case UPH_IS_SPELL_CAPITALIZATION        : bReadOnly = rOpt.bROIsSpellCapitalization    ; break;
        case UPH_IS_SPELL_WITH_DIGITS           : bReadOnly = rOpt.bROIsSpellWithDigits        ; break;
        case UPH_IS_SPELL_UPPER_CASE            : bReadOnly = rOpt.bROIsSpellUpperCase         ; break;
        case UPH_HYPH_MIN_LEADING               : bReadOnly = rOpt.bROHyphMinLeading           ; break;
        case UPH_HYPH_MIN_TRAILING              : bReadOnly = rOpt.bROHyphMinTrailing          ; break;
        case UPH_HYPH_MIN_WORD_LENGTH           : bReadOnly = rOpt.bROHyphMinWordLength        ; break;
        case UPH_ACTIVE_DICTIONARIES            : bReadOnly = rOpt.bROActiveDics               ; break;
        case UPH_ACTIVE_CONVERSION_DICTIONARIES : bReadOnly = rOpt.bROActiveConvDics           ; break;
        case UPH_DEFAULT_LOCALE                 : bReadOnly = rOpt.bRODefaultLanguage          ; break;
        case UPH_DEFAULT_LOCALE_CJK             : bReadOnly = rOpt.bRODefaultLanguage_CJK      ; break;
        case UPH_DEFAULT_LOCALE_CTL             : bReadOnly = rOpt.bRODefaultLanguage_CTL      ; break;
        case UPH_IS_IGNORE_POST_POSITIONAL_WORD :       bReadOnly = rOpt.bROIsIgnorePostPositionalWord; break;
        case UPH_IS_AUTO_CLOSE_DIALOG :                 bReadOnly = rOpt.bROIsAutoCloseDialog; break;
        case UPH_IS_SHOW_ENTRIES_RECENTLY_USED_FIRST :  bReadOnly = rOpt.bROIsShowEntriesRecentlyUsedFirst; break;
        case UPH_IS_AUTO_REPLACE_UNIQUE_ENTRIES :       bReadOnly = rOpt.bROIsAutoReplaceUniqueEntries; break;
        case UPH_IS_DIRECTION_TO_SIMPLIFIED : bReadOnly = rOpt.bROIsDirectionToSimplified; break;
        case UPH_IS_USE_CHARACTER_VARIANTS : bReadOnly = rOpt.bROIsUseCharacterVariants; break;
        case UPH_IS_TRANSLATE_COMMON_TERMS : bReadOnly = rOpt.bROIsTranslateCommonTerms; break;
        case UPH_IS_REVERSE_MAPPING :        bReadOnly = rOpt.bROIsReverseMapping; break;
        case UPH_DATA_FILES_CHANGED_CHECK_VALUE :       bReadOnly = rOpt.bRODataFilesChangedCheckValue; break;
        case UPH_IS_GRAMMAR_AUTO:                       bReadOnly = rOpt.bROIsGrammarAuto; break;
        case UPH_IS_GRAMMAR_INTERACTIVE:                bReadOnly = rOpt.bROIsGrammarInteractive; break;
        default :
            DBG_ASSERT( 0, "unexpected property handle" );
    }
    return bReadOnly;
}

//////////////////////////////////////////////////////////////////////

static SvtLinguConfigItem *pCfgItem = 0;
static sal_Int32           nCfgItemRefCount = 0;

static const rtl::OUString aG_SupportedDictionaryFormats( A2OU("SupportedDictionaryFormats") );
static const rtl::OUString aG_Dictionaries( A2OU("Dictionaries") );
static const rtl::OUString aG_Locations( A2OU("Locations") );
static const rtl::OUString aG_Format( A2OU("Format") );
static const rtl::OUString aG_Locales( A2OU("Locales") );
static const rtl::OUString aG_DisabledDictionaries( A2OU("DisabledDictionaries") );
static const rtl::OUString aG_LastActiveDictionaries( A2OU("LastActiveDictionaries") );

SvtLinguConfig::SvtLinguConfig()
{
    // Global access, must be guarded (multithreading)
    osl::MutexGuard aGuard( GetOwnMutex() );
    ++nCfgItemRefCount;
}


SvtLinguConfig::~SvtLinguConfig()
{
    osl::MutexGuard aGuard( GetOwnMutex() );

    if (pCfgItem && pCfgItem->IsModified())
        pCfgItem->Commit();

    if (--nCfgItemRefCount <= 0)
    {
        if (pCfgItem)
            delete pCfgItem;
        pCfgItem = 0;
    }
}


SvtLinguConfigItem & SvtLinguConfig::GetConfigItem()
{
    // Global access, must be guarded (multithreading)
    osl::MutexGuard aGuard( GetOwnMutex() );
    if (!pCfgItem)
    {
        pCfgItem = new SvtLinguConfigItem;
        ItemHolder1::holdConfigItem(E_LINGUCFG);
    }
    return *pCfgItem;
}


uno::Sequence< OUString > SvtLinguConfig::GetNodeNames( const OUString &rNode )
{
    return GetConfigItem().GetNodeNames( rNode );
}


uno::Sequence< uno::Any > SvtLinguConfig::GetProperties( const uno::Sequence< OUString > &rNames )
{
    return GetConfigItem().GetProperties(rNames);
}


sal_Bool SvtLinguConfig::ReplaceSetProperties(
        const OUString &rNode, uno::Sequence< beans::PropertyValue > rValues )
{
    return GetConfigItem().ReplaceSetProperties( rNode, rValues );
}


uno::Any SvtLinguConfig::GetProperty( const OUString &rPropertyName ) const
{
    return GetConfigItem().GetProperty( rPropertyName );
}


uno::Any SvtLinguConfig::GetProperty( sal_Int32 nPropertyHandle ) const
{
    return GetConfigItem().GetProperty( nPropertyHandle );
}


sal_Bool SvtLinguConfig::SetProperty( const OUString &rPropertyName, const uno::Any &rValue )
{
    return GetConfigItem().SetProperty( rPropertyName, rValue );
}


sal_Bool SvtLinguConfig::SetProperty( sal_Int32 nPropertyHandle, const uno::Any &rValue )
{
    return GetConfigItem().SetProperty( nPropertyHandle, rValue );
}


sal_Bool SvtLinguConfig::GetOptions( SvtLinguOptions &rOptions ) const
{
    return GetConfigItem().GetOptions( rOptions );
}


sal_Bool SvtLinguConfig::SetOptions( const SvtLinguOptions &rOptions )
{
    return GetConfigItem().SetOptions( rOptions );
}


sal_Bool SvtLinguConfig::IsReadOnly( const rtl::OUString &rPropertyName ) const
{
    return GetConfigItem().IsReadOnly( rPropertyName );
}

sal_Bool SvtLinguConfig::IsReadOnly( sal_Int32 nPropertyHandle ) const
{
    return GetConfigItem().IsReadOnly( nPropertyHandle );
}

sal_Bool SvtLinguConfig::GetElementNamesFor(
     const rtl::OUString &rNodeName,
     uno::Sequence< rtl::OUString > &rElementNames ) const
{
    bool bSuccess = false;
    try
    {
        uno::Reference< container::XNameAccess > xNA( GetMainUpdateAccess(), uno::UNO_QUERY_THROW );
        xNA.set( xNA->getByName( A2OU("ServiceManager") ), uno::UNO_QUERY_THROW );
        xNA.set( xNA->getByName( rNodeName ), uno::UNO_QUERY_THROW );
        rElementNames = xNA->getElementNames();
        bSuccess = true;
    }
    catch (uno::Exception &)
    {
    }
    return bSuccess;
}

static uno::Reference< container::XNameAccess > GetOrCreateSetEntry_Impl(
    const uno::Reference< container::XNameAccess > &rxSetNameAccess,
    const rtl::OUString &rEntryName )
{
    uno::Reference< container::XNameAccess > xResult;
    try
    {
        if (!rxSetNameAccess->hasByName( rEntryName ))
        {
            uno::Reference< lang::XSingleServiceFactory > xFactory( rxSetNameAccess, uno::UNO_QUERY_THROW);
            uno::Reference< uno::XInterface > xNewEntry( xFactory->createInstance() );
            uno::Reference< container::XNameContainer > xNC( rxSetNameAccess, uno::UNO_QUERY_THROW );
            xNC->insertByName( rEntryName, makeAny( xNewEntry ) );
        }
        xResult.set( rxSetNameAccess->getByName( rEntryName ), uno::UNO_QUERY_THROW );
    }
    catch (uno::Exception &)
    {
    }
    return xResult;
}

sal_Bool SvtLinguConfig::GetSupportedDictionaryFormatsFor(
    const rtl::OUString &rSetName,
    const rtl::OUString &rSetEntry,
    uno::Sequence< rtl::OUString > &rFormatList ) const
{
    if (rSetName.getLength() == 0 || rSetEntry.getLength() == 0)
        return sal_False;
    bool bSuccess = false;
    try
    {
        uno::Reference< container::XNameAccess > xNA( GetMainUpdateAccess(), uno::UNO_QUERY_THROW );
        xNA.set( xNA->getByName( A2OU("ServiceManager") ), uno::UNO_QUERY_THROW );
        xNA.set( xNA->getByName( rSetName ), uno::UNO_QUERY_THROW );
        xNA.set( xNA->getByName( rSetEntry ), uno::UNO_QUERY_THROW );
        if (xNA->getByName( aG_SupportedDictionaryFormats ) >>= rFormatList)
            bSuccess = true;
        DBG_ASSERT( rFormatList.getLength(), "supported dictionary format list is empty" );
    }
    catch (uno::Exception &)
    {
    }
    return bSuccess;
}

void SvtLinguConfig::SetOrCreateSupportedDictionaryFormatsFor(
    const rtl::OUString &rSetName,
    const rtl::OUString &rSetEntry,
    const uno::Sequence< rtl::OUString > &rFormatList  ) const
{
    if (rSetName.getLength() == 0 || rSetEntry.getLength() == 0)
        return;
    try
    {
        DBG_ASSERT( rFormatList.getLength(), "applying empty format list. Really??" );

        uno::Reference< util::XChangesBatch > xUpdateAccess( GetMainUpdateAccess() );
        uno::Reference< container::XNameAccess > xNA( xUpdateAccess, uno::UNO_QUERY_THROW );
        xNA.set( xNA->getByName( A2OU("ServiceManager") ), uno::UNO_QUERY_THROW );
        xNA.set( xNA->getByName( rSetName ), uno::UNO_QUERY_THROW );
        xNA = GetOrCreateSetEntry_Impl( xNA, rSetEntry );

        uno::Reference< container::XNameReplace > xNR( xNA, uno::UNO_QUERY_THROW );
        xNR->replaceByName( aG_SupportedDictionaryFormats, uno::makeAny( rFormatList ) );

        xUpdateAccess->commitChanges();
    }
    catch (uno::Exception &)
    {
    }
}


static uno::WeakReference< util::XMacroExpander > aG_xMacroExpander;

static uno::Reference< util::XMacroExpander > lcl_GetMacroExpander()
{
    uno::Reference< util::XMacroExpander > xMacroExpander( aG_xMacroExpander );
    if ( !xMacroExpander.is() )
    {
        if ( !xMacroExpander.is() )
        {
            uno::Reference< uno::XComponentContext > xContext;
            uno::Reference< beans::XPropertySet > xProps( ::comphelper::getProcessServiceFactory(), uno::UNO_QUERY );
            xProps->getPropertyValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "DefaultContext" ))) >>= xContext;
            if ( xContext.is() )
            {
                aG_xMacroExpander =  uno::Reference< com::sun::star::util::XMacroExpander >( xContext->getValueByName(
                                        OUString( RTL_CONSTASCII_USTRINGPARAM( "/singletons/com.sun.star.util.theMacroExpander"))),
                                        uno::UNO_QUERY );
                xMacroExpander = aG_xMacroExpander;
            }
        }
    }

    return xMacroExpander;
}


static bool lcl_GetFileUrlFromOrigin(
    OUString /*out*/ &rFileUrl,
    const OUString &rOrigin,
    uno::Reference< util::XMacroExpander > &rxMacroExpander )
{
    bool bSuccess = false;
    if (rOrigin.getLength() > 0 && rxMacroExpander.is())
    {
        rtl::OUString aURL( rOrigin );
        if (( aURL.compareToAscii( RTL_CONSTASCII_STRINGPARAM( EXPAND_PROTOCOL )) == 0 ) &&
            rxMacroExpander.is() )
        {
            // cut protocol
            OUString aMacro( aURL.copy( sizeof ( EXPAND_PROTOCOL ) -1 ) );
            // decode uric class chars
            aMacro = Uri::decode( aMacro, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8 );
            // expand macro string
            aURL = rxMacroExpander->expandMacros( aMacro );

            bool bIsFileUrl = aURL.compareToAscii( RTL_CONSTASCII_STRINGPARAM( FILE_PROTOCOL )) == 0;
            if (bIsFileUrl)
            {
                rFileUrl = aURL;
                bSuccess = true;
            }
            else
            {
                DBG_ASSERT( bIsFileUrl, "not a file URL");
            }
        }
        else
        {
            DBG_ASSERT( 0, "failed to get file URL" );
        }
    }
    return bSuccess;
}


sal_Bool SvtLinguConfig::GetDictionaryEntry(
    const rtl::OUString &rNodeName,
    SvtLinguConfigDictionaryEntry &rDicEntry ) const
{
    if (rNodeName.getLength() == 0)
        return sal_False;
    bool bSuccess = false;
    try
    {
        uno::Reference< container::XNameAccess > xNA( GetMainUpdateAccess(), uno::UNO_QUERY_THROW );
        xNA.set( xNA->getByName( A2OU("ServiceManager") ), uno::UNO_QUERY_THROW );
        xNA.set( xNA->getByName( aG_Dictionaries ), uno::UNO_QUERY_THROW );
        xNA.set( xNA->getByName( rNodeName ), uno::UNO_QUERY_THROW );

        // read group data...
        uno::Sequence< rtl::OUString >	aLocations;
        rtl::OUString					aFormatName;
		uno::Sequence< rtl::OUString >  aLocaleNames;
        bSuccess =  (xNA->getByName( aG_Locations ) >>= aLocations)  &&
                    (xNA->getByName( aG_Format )    >>= aFormatName) &&
                    (xNA->getByName( aG_Locales )   >>= aLocaleNames);
        DBG_ASSERT( aLocations.getLength(), "Dictionary locations not set" );
        DBG_ASSERT( aFormatName.getLength(), "Dictionary format name not set" );
        DBG_ASSERT( aLocaleNames.getLength(), "No locales set for the dictionary" );

        // if sucessful continue
        if (bSuccess)
        {
            // get file URL's for the locations
            uno::Reference< util::XMacroExpander > xMacroExpander( lcl_GetMacroExpander() );
            for (sal_Int32 i = 0;  i < aLocations.getLength();  ++i)
            {
                rtl::OUString &rLocation = aLocations[i];
                if (!lcl_GetFileUrlFromOrigin( rLocation, rLocation, xMacroExpander ))
                    bSuccess = false;
            }

            // if everything was fine return the result
            if (bSuccess)
            {
                rDicEntry.aLocations    = aLocations;
                rDicEntry.aFormatName   = aFormatName;
                rDicEntry.aLocaleNames  = aLocaleNames;
            }
        }
    }
    catch (uno::Exception &)
    {
    }
    return bSuccess;
}

void SvtLinguConfig::SetOrCreateDictionaryEntry(
    const rtl::OUString &rNodeName,
    const SvtLinguConfigDictionaryEntry &rDicEntry ) const
{
    if (rNodeName.getLength() == 0)
        return;
    try
    {
        uno::Reference< util::XChangesBatch > xUpdateAccess( GetMainUpdateAccess() );
        uno::Reference< container::XNameAccess > xNA( xUpdateAccess, uno::UNO_QUERY_THROW );
        xNA.set( xNA->getByName( A2OU("ServiceManager") ), uno::UNO_QUERY_THROW );
        xNA.set( xNA->getByName( aG_Dictionaries ), uno::UNO_QUERY_THROW );
        xNA = GetOrCreateSetEntry_Impl( xNA, rNodeName );

        DBG_ASSERT( rDicEntry.aLocations.getLength(), "Applying empty dictionary locations. Really correct??" );
        DBG_ASSERT( rDicEntry.aFormatName.getLength(), "Applying empty dictionary format name. Really correct??" );
        DBG_ASSERT( rDicEntry.aLocaleNames.getLength(), "Applying empty list of locales for the dictionary. Really correct??" );

        uno::Reference< container::XNameReplace > xNR( xNA, uno::UNO_QUERY_THROW );
        xNR->replaceByName( aG_Locations, uno::makeAny( rDicEntry.aLocations ) );
        xNR->replaceByName( aG_Format,    uno::makeAny( rDicEntry.aFormatName ) );
        xNR->replaceByName( aG_Locales,   uno::makeAny( rDicEntry.aLocaleNames ) );

        xUpdateAccess->commitChanges();
    }
    catch (uno::Exception &)
    {
    }
}

uno::Sequence< rtl::OUString > SvtLinguConfig::GetDisabledDictionaries() const
{
    uno::Sequence< rtl::OUString > aResult;
    try
    {
        uno::Reference< container::XNameAccess > xNA( GetMainUpdateAccess(), uno::UNO_QUERY_THROW );
        xNA.set( xNA->getByName( A2OU("ServiceManager") ), uno::UNO_QUERY_THROW );
        xNA->getByName( aG_DisabledDictionaries ) >>= aResult;
    }
    catch (uno::Exception &)
    {
    }
    return aResult;
}

void SvtLinguConfig::SetDisabledDictionaries(
    const uno::Sequence< rtl::OUString > &rDictionaries ) const
{
    try
    {
        uno::Reference< util::XChangesBatch > xUpdateAccess( GetMainUpdateAccess() );
        uno::Reference< container::XNameAccess > xNA( xUpdateAccess, uno::UNO_QUERY_THROW );
        xNA.set( xNA->getByName( A2OU("ServiceManager") ), uno::UNO_QUERY_THROW );
        if (xNA->hasByName( aG_DisabledDictionaries ))
        {
            uno::Reference< container::XNameReplace > xNR( xNA, uno::UNO_QUERY_THROW );
            xNR->replaceByName( aG_DisabledDictionaries, makeAny( rDictionaries ) );
        }
        else
        {
            uno::Reference< container::XNameContainer > xNC( xNA, uno::UNO_QUERY_THROW );
            xNC->insertByName( aG_DisabledDictionaries, makeAny( rDictionaries ) );
        }

        xUpdateAccess->commitChanges();
    }
    catch (uno::Exception &)
    {
    }
}

std::vector< SvtLinguConfigDictionaryEntry > SvtLinguConfig::GetActiveDictionariesByFormat(
    const rtl::OUString &rFormatName )
{
    std::vector< SvtLinguConfigDictionaryEntry > aRes;
    if (rFormatName.getLength() == 0)
        return aRes;

    try
    {
        uno::Sequence< rtl::OUString > aElementNames;
        GetElementNamesFor( aG_Dictionaries, aElementNames );
        sal_Int32 nLen = aElementNames.getLength();
        const rtl::OUString *pElementNames = aElementNames.getConstArray();

        SvtLinguConfigDictionaryEntry aDicEntry;
        for (sal_Int32 i = 0;  i < nLen;  ++i)
        {
            // does dictionary match the format we are looking for?
            if (GetDictionaryEntry( pElementNames[i], aDicEntry ) &&
                aDicEntry.aFormatName == rFormatName)
            {
                // check if it is active or not
                bool bDicIsActive = true;
                const uno::Sequence< rtl::OUString > aDisabledDics( GetDisabledDictionaries() );
                for (sal_Int32 k = 0;  bDicIsActive && k < aDisabledDics.getLength();  ++k)
                {
                    if (aDisabledDics[k] == pElementNames[i])
                        bDicIsActive = false;
                }

                if (bDicIsActive)
                {
                    DBG_ASSERT( aDicEntry.aFormatName.getLength(),
                            "FormatName not set" );
                    DBG_ASSERT( aDicEntry.aLocations.getLength(),
                            "Locations not set" );
                    DBG_ASSERT( aDicEntry.aLocaleNames.getLength(),
                            "Locales not set" );
                    aRes.push_back( aDicEntry );
                }
            }
        }
    }
    catch (uno::Exception &)
    {
    }

    return aRes;
}


uno::Reference< util::XChangesBatch > SvtLinguConfig::GetMainUpdateAccess() const
{
    if (!m_xMainUpdateAccess.is())
    {
        try
        {
            // get configuration provider
            uno::Reference< lang::XMultiServiceFactory > xConfigurationProvider;
            uno::Reference< lang::XMultiServiceFactory > xMgr = comphelper::getProcessServiceFactory();
            if (xMgr.is())
            {
                xConfigurationProvider = uno::Reference< lang::XMultiServiceFactory > (
                        xMgr->createInstance( OUString( RTL_CONSTASCII_USTRINGPARAM(
                            "com.sun.star.configuration.ConfigurationProvider" ) ) ),
                        uno::UNO_QUERY_THROW ) ;
            }

            // get configuration update access
            beans::PropertyValue aValue;
            aValue.Name  = A2OU( "nodepath" );
            aValue.Value = uno::makeAny( A2OU("org.openoffice.Office.Linguistic") );
            uno::Sequence< uno::Any > aProps(1);
            aProps[0] <<= aValue;
            m_xMainUpdateAccess = uno::Reference< util::XChangesBatch >(
                    xConfigurationProvider->createInstanceWithArguments(
                        A2OU( "com.sun.star.configuration.ConfigurationUpdateAccess" ), aProps ),
                        uno::UNO_QUERY_THROW );
        }
        catch (uno::Exception &)
        {
        }
    }

    return m_xMainUpdateAccess;
}


rtl::OUString SvtLinguConfig::GetVendorImageUrl_Impl(
    const rtl::OUString &rServiceImplName,
    const rtl::OUString &rImageName ) const
{
    rtl::OUString aRes;
    try
    {
        uno::Reference< container::XNameAccess > xImagesNA( GetMainUpdateAccess(), uno::UNO_QUERY_THROW );
        xImagesNA.set( xImagesNA->getByName( A2OU("Images") ), uno::UNO_QUERY_THROW );

        uno::Reference< container::XNameAccess > xNA( xImagesNA->getByName( A2OU("ServiceNameEntries") ), uno::UNO_QUERY_THROW );
        xNA.set( xNA->getByName( rServiceImplName ), uno::UNO_QUERY_THROW );
        uno::Any aAny( xNA->getByName( A2OU("VendorImagesNode") ) );
        rtl::OUString aVendorImagesNode;
        if (aAny >>= aVendorImagesNode)
        {
            xNA = xImagesNA;
            xNA.set( xNA->getByName( A2OU("VendorImages") ), uno::UNO_QUERY_THROW );
            xNA.set( xNA->getByName( aVendorImagesNode ), uno::UNO_QUERY_THROW );
            aAny = xNA->getByName( rImageName );
            rtl::OUString aTmp;
            if (aAny >>= aTmp)
            {
                uno::Reference< util::XMacroExpander > xMacroExpander( lcl_GetMacroExpander() );
                if (lcl_GetFileUrlFromOrigin( aTmp, aTmp, xMacroExpander ))
                    aRes = aTmp;
            }
        }
    }
    catch (uno::Exception &)
    {
        DBG_ASSERT( 0, "exception caught. GetVendorImageUrl_Impl failed" );
    }
    return aRes;
}


rtl::OUString SvtLinguConfig::GetSpellAndGrammarDialogImage(
    const rtl::OUString &rServiceImplName,
    bool bHighContrast ) const
{
    rtl::OUString   aRes;
    if (rServiceImplName.getLength() > 0)
    {
        rtl::OUString aImageName( A2OU( bHighContrast ? "SpellAndGrammarDialogImage_HC" : "SpellAndGrammarDialogImage" ));
        rtl::OUString aPath( GetVendorImageUrl_Impl( rServiceImplName, aImageName ) );
        aRes = aPath;
    }
    return aRes;
}


rtl::OUString SvtLinguConfig::GetSpellAndGrammarContextSuggestionImage(
    const rtl::OUString &rServiceImplName,
    bool bHighContrast ) const
{
    rtl::OUString   aRes;
    if (rServiceImplName.getLength() > 0)
    {
        rtl::OUString aImageName( A2OU( bHighContrast ? "SpellAndGrammarContextMenuSuggestionImage_HC" : "SpellAndGrammarContextMenuSuggestionImage" ));
        rtl::OUString aPath( GetVendorImageUrl_Impl( rServiceImplName, aImageName ) );
        aRes = aPath;
    }
    return aRes;
}


rtl::OUString SvtLinguConfig::GetSpellAndGrammarContextDictionaryImage(
    const rtl::OUString &rServiceImplName,
    bool bHighContrast ) const
{
    rtl::OUString   aRes;
    if (rServiceImplName.getLength() > 0)
    {
        rtl::OUString aImageName( A2OU( bHighContrast ? "SpellAndGrammarContextMenuDictionaryImage_HC" : "SpellAndGrammarContextMenuDictionaryImage" ));
        rtl::OUString aPath( GetVendorImageUrl_Impl( rServiceImplName, aImageName ) );
        aRes = aPath;
    }
    return aRes;
}


::rtl::OUString SvtLinguConfig::GetThesaurusDialogImage( 
    const ::rtl::OUString &rServiceImplName, 
    bool bHighContrast ) const
{
    rtl::OUString   aRes;
    if (rServiceImplName.getLength() > 0)
    {
        rtl::OUString aImageName( A2OU( bHighContrast ? "ThesaurusDialogImage_HC" : "ThesaurusDialogImage" ));
        rtl::OUString aPath( GetVendorImageUrl_Impl( rServiceImplName, aImageName ) );
        aRes = aPath;
    }
    return aRes;
}

    
::rtl::OUString SvtLinguConfig::GetSynonymsContextImage( 
    const ::rtl::OUString &rServiceImplName, 
    bool bHighContrast ) const
{
    rtl::OUString   aRes;
    if (rServiceImplName.getLength() > 0)
    {
        rtl::OUString aImageName( A2OU( bHighContrast ? "SynonymsContextMenuImage_HC" : "SynonymsContextMenuImage" ));
        rtl::OUString aPath( GetVendorImageUrl_Impl( rServiceImplName, aImageName ) );
        aRes = aPath;
    }
    return aRes;
}
    

bool SvtLinguConfig::HasVendorImages( const char *pImageName ) const
{
    bool bRes = false;
    if (pImageName)
    {
        try
        {
            uno::Reference< container::XNameAccess > xNA( GetMainUpdateAccess(), uno::UNO_QUERY_THROW );
            xNA.set( xNA->getByName( A2OU("Images") ), uno::UNO_QUERY_THROW );
            xNA.set( xNA->getByName( A2OU("VendorImages") ), uno::UNO_QUERY_THROW );

            uno::Sequence< rtl::OUString > aElementNames( xNA->getElementNames() );
            sal_Int32 nVendors = aElementNames.getLength();
            const rtl::OUString *pVendor = aElementNames.getConstArray();
            for (sal_Int32 i = 0;  i < nVendors;  ++i)
            {
                uno::Reference< container::XNameAccess > xNA2( xNA->getByName( pVendor[i] ), uno::UNO_QUERY_THROW  );
                uno::Sequence< rtl::OUString > aPropNames( xNA2->getElementNames() );
                sal_Int32 nProps = aPropNames.getLength();
                const rtl::OUString *pPropNames = aPropNames.getConstArray();
                for (sal_Int32 k = 0;  k < nProps;  ++k)
                {
                    // for a quicker check we ignore the HC image names here
                    const OUString &rName = pPropNames[k];
                    if (rName.equalsAscii( pImageName ))
                    {
                        bRes = true;
                        break;
                    }    
                }    
            }    
        }
        catch (uno::Exception &)
        {
            DBG_ASSERT( 0, "exception caught. HasVendorImages failed" );
        }
    }
    return bRes;
}
    

bool SvtLinguConfig::HasGrammarChecker() const
{
	bool bRes = false;

	try
	{
        uno::Reference< container::XNameAccess > xNA( GetMainUpdateAccess(), uno::UNO_QUERY_THROW );
        xNA.set( xNA->getByName( A2OU("ServiceManager") ), uno::UNO_QUERY_THROW );
		xNA.set( xNA->getByName( A2OU("GrammarCheckerList") ), uno::UNO_QUERY_THROW );

		uno::Sequence< rtl::OUString > aElementNames( xNA->getElementNames() );
		bRes = aElementNames.getLength() > 0;
	}
	catch (uno::Exception &)
	{
	}

    return bRes;
}

//////////////////////////////////////////////////////////////////////