/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   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 .
 */
 
#include <sal/config.h>
 
#include <com/sun/star/io/IOException.hpp>
#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
#include <osl/diagnose.h>
#include <vcl/svapp.hxx>
#include <svtools/unoevent.hxx>
#include <svl/urihelper.hxx>
#include <sfx2/event.hxx>
#include <swtypes.hxx>
#include <glosdoc.hxx>
#include <shellio.hxx>
#include <initui.hxx>
#include <gloslst.hxx>
#include <unoatxt.hxx>
#include <unomap.hxx>
#include <unomid.h>
#include <unotextbodyhf.hxx>
#include <unotextrange.hxx>
#include <TextCursorHelper.hxx>
#include <swevent.hxx>
#include <doc.hxx>
#include <unocrsr.hxx>
#include <IMark.hxx>
#include <IDocumentContentOperations.hxx>
#include <IDocumentRedlineAccess.hxx>
#include <IDocumentFieldsAccess.hxx>
#include <IDocumentState.hxx>
#include <unoprnms.hxx>
#include <docsh.hxx>
#include <swmodule.hxx>
#include <swdll.hxx>
#include <svl/hint.hxx>
#include <svl/macitem.hxx>
#include <editeng/acorrcfg.hxx>
#include <comphelper/servicehelper.hxx>
#include <cppuhelper/exc_hlp.hxx>
#include <cppuhelper/supportsservice.hxx>
 
#include <memory>
 
using namespace ::com::sun::star;
 
SwXAutoTextContainer::SwXAutoTextContainer()
{
    pGlossaries = ::GetGlossaries();
 
}
 
SwXAutoTextContainer::~SwXAutoTextContainer()
{
 
}
 
sal_Int32 SwXAutoTextContainer::getCount()
{
    OSL_ENSURE(pGlossaries->GetGroupCnt() < static_cast<size_t>(SAL_MAX_INT32),
               "SwXAutoTextContainer::getCount: too many items");
    return static_cast<sal_Int32>(pGlossaries->GetGroupCnt());
}
 
uno::Any SwXAutoTextContainer::getByIndex(sal_Int32 nIndex)
{
    SolarMutexGuard aGuard;
    const size_t nCount = pGlossaries->GetGroupCnt();
    if ( nIndex < 0 || static_cast<size_t>(nIndex) >= nCount )
        throw lang::IndexOutOfBoundsException();
    return getByName(pGlossaries->GetGroupName( static_cast<size_t>(nIndex) ));
}
 
uno::Type SwXAutoTextContainer::getElementType()
{
    return cppu::UnoType<text::XAutoTextGroup>::get();
 
}
 
sal_Bool SwXAutoTextContainer::hasElements()
{
    // At least standard should always exists!
    return true;
}
 
uno::Any SwXAutoTextContainer::getByName(const OUString& GroupName)
{
    SolarMutexGuard aGuard;
 
    uno::Reference< text::XAutoTextGroup > xGroup;
    if ( pGlossaries && hasByName( GroupName ) )    // group name already known?
        // true = create group if not already available
        xGroup = pGlossaries->GetAutoTextGroup( GroupName );
 
    if ( !xGroup.is() )
        throw container::NoSuchElementException();
 
    return makeAny( xGroup );
}
 
uno::Sequence< OUString > SwXAutoTextContainer::getElementNames()
{
    SolarMutexGuard aGuard;
    const size_t nCount = pGlossaries->GetGroupCnt();
    OSL_ENSURE(nCount < static_cast<size_t>(SAL_MAX_INT32),
               "SwXAutoTextContainer::getElementNames: too many groups");
 
    uno::Sequence< OUString > aGroupNames(static_cast<sal_Int32>(nCount));
    OUString *pArr = aGroupNames.getArray();
 
    for ( size_t i = 0; i < nCount; ++i )
    {
        // The names will be passed without a path extension.
        OUString sGroupName(pGlossaries->GetGroupName(i));
        pArr[i] = sGroupName.getToken(0, GLOS_DELIM);
    }
    return aGroupNames;
}
// Finds group names with or without path index.
sal_Bool SwXAutoTextContainer::hasByName(const OUString& Name)
{
    SolarMutexGuard aGuard;
    OUString sGroupName( pGlossaries->GetCompleteGroupName( Name ) );
    if(!sGroupName.isEmpty())
        return true;
    return false;
}
 
uno::Reference< text::XAutoTextGroup >  SwXAutoTextContainer::insertNewByName(
    const OUString& aGroupName)
{
    SolarMutexGuard aGuard;
    if(hasByName(aGroupName))
        throw container::ElementExistException();
    //check for non-ASCII characters
    if(aGroupName.isEmpty())
    {
        lang::IllegalArgumentException aIllegal;
        aIllegal.Message = "group name must not be empty";
        throw aIllegal;
    }
    for(sal_Int32 nPos = 0; nPos < aGroupName.getLength(); nPos++)
    {
        sal_Unicode cChar = aGroupName[nPos];
        if (rtl::isAsciiAlphanumeric(cChar) ||
            (cChar == '_') ||
            (cChar == 0x20) ||
            (cChar == GLOS_DELIM) )
        {
            continue;
        }
        lang::IllegalArgumentException aIllegal;
        aIllegal.Message = "group name must contain a-z, A-z, '_', ' ' only";
        throw aIllegal;
    }
    OUString sGroup(aGroupName);
    if (sGroup.indexOf(GLOS_DELIM)<0)
    {
        sGroup += OUStringLiteral1(GLOS_DELIM) + "0";
    }
    pGlossaries->NewGroupDoc(sGroup, sGroup.getToken(0, GLOS_DELIM));
 
    uno::Reference< text::XAutoTextGroup > xGroup = pGlossaries->GetAutoTextGroup( sGroup );
    OSL_ENSURE( xGroup.is(), "SwXAutoTextContainer::insertNewByName: no UNO object created? How this?" );
        // We just inserted the group into the glossaries, so why doesn't it exist?
 
    return xGroup;
}
 
void SwXAutoTextContainer::removeByName(const OUString& aGroupName)
{
    SolarMutexGuard aGuard;
    // At first find the name with path extension
    OUString sGroupName = pGlossaries->GetCompleteGroupName( aGroupName );
    if(sGroupName.isEmpty())
        throw container::NoSuchElementException();
    pGlossaries->DelGroupDoc(sGroupName);
}
 
OUString SwXAutoTextContainer::getImplementationName()
{
    return OUString("SwXAutoTextContainer" );
}
 
sal_Bool SwXAutoTextContainer::supportsService(const OUString& rServiceName)
{
    return cppu::supportsService(this, rServiceName);
}
 
uno::Sequence< OUString > SwXAutoTextContainer::getSupportedServiceNames()
{
    OUString sService("com.sun.star.text.AutoTextContainer");
    const uno::Sequence< OUString > aSeq( &sService, 1 );
    return aSeq;
}
 
namespace
{
    class theSwXAutoTextGroupUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXAutoTextGroupUnoTunnelId > {};
}
 
const uno::Sequence< sal_Int8 > & SwXAutoTextGroup::getUnoTunnelId()
{
    return theSwXAutoTextGroupUnoTunnelId::get().getSeq();
}
 
sal_Int64 SAL_CALL SwXAutoTextGroup::getSomething( const uno::Sequence< sal_Int8 >& rId )
{
    if( rId.getLength() == 16
        && 0 == memcmp( getUnoTunnelId().getConstArray(),
                                        rId.getConstArray(), 16 ) )
    {
            return sal::static_int_cast< sal_Int64 >( reinterpret_cast< sal_IntPtr >( this ));
    }
    return 0;
}
 
SwXAutoTextGroup::SwXAutoTextGroup(const OUString& rName,
            SwGlossaries*   pGlos) :
    pPropSet(aSwMapProvider.GetPropertySet(PROPERTY_MAP_AUTO_TEXT_GROUP)),
    pGlossaries(pGlos),
    sName(rName),
    m_sGroupName(rName)
{
    OSL_ENSURE( -1 != rName.indexOf( GLOS_DELIM ),
        "SwXAutoTextGroup::SwXAutoTextGroup: to be constructed with a complete name only!" );
}
 
SwXAutoTextGroup::~SwXAutoTextGroup()
{
}
 
uno::Sequence< OUString > SwXAutoTextGroup::getTitles()
{
    SolarMutexGuard aGuard;
    std::unique_ptr<SwTextBlocks> pGlosGroup(pGlossaries ? pGlossaries->GetGroupDoc(m_sGroupName) : nullptr);
    if (!pGlosGroup || pGlosGroup->GetError())
        throw uno::RuntimeException();
    const sal_uInt16 nCount = pGlosGroup->GetCount();
 
    uno::Sequence< OUString > aEntryTitles(nCount);
    OUString *pArr = aEntryTitles.getArray();
 
    for ( sal_uInt16 i = 0; i < nCount; i++ )
        pArr[i] = pGlosGroup->GetLongName(i);
    return aEntryTitles;
}
 
void SwXAutoTextGroup::renameByName(const OUString& aElementName,
    const OUString& aNewElementName, const OUString& aNewElementTitle)
{
    SolarMutexGuard aGuard;
    // throw exception only if the programmatic name is to be changed into an existing name
    if(aNewElementName != aElementName && hasByName(aNewElementName))
        throw container::ElementExistException();
    std::unique_ptr<SwTextBlocks> pGlosGroup(pGlossaries ? pGlossaries->GetGroupDoc(m_sGroupName) : nullptr);
    if(!pGlosGroup || pGlosGroup->GetError())
        throw uno::RuntimeException();
 
    sal_uInt16 nIdx = pGlosGroup->GetIndex( aElementName);
    if(USHRT_MAX == nIdx)
        throw lang::IllegalArgumentException();
    OUString aNewShort(aNewElementName);
    OUString aNewName(aNewElementTitle);
    sal_uInt16 nOldLongIdx = pGlosGroup->GetLongIndex( aNewShort );
    sal_uInt16 nOldIdx = pGlosGroup->GetIndex( aNewName );
 
    if( nIdx != USHRT_MAX &&
            (nOldLongIdx == USHRT_MAX || nOldLongIdx == nIdx )&&
                (nOldIdx == USHRT_MAX || nOldIdx == nIdx ))
    {
        pGlosGroup->Rename( nIdx, &aNewShort, &aNewName );
        if(pGlosGroup->GetError() != ERRCODE_NONE)
            throw io::IOException();
    }
 
}
 
static bool lcl_CopySelToDoc( SwDoc* pInsDoc, OTextCursorHelper* pxCursor, SwXTextRange* pxRange)
{
    OSL_ENSURE( pInsDoc, "no InsDoc");
 
    SwNodes& rNds = pInsDoc->GetNodes();
 
    SwNodeIndex aIdx( rNds.GetEndOfContent(), -1 );
    SwContentNode * pNd = aIdx.GetNode().GetContentNode();
    SwPosition aPos(aIdx, SwIndex(pNd, pNd ? pNd->Len() : 0));
 
    bool bRet = false;
    pInsDoc->getIDocumentFieldsAccess().LockExpFields();
    {
        SwDoc *const pDoc(pxCursor ? pxCursor->GetDoc() : &pxRange->GetDoc());
        SwPaM aPam(pDoc->GetNodes());
        SwPaM * pPam(nullptr);
        if(pxCursor)
        {
            pPam = pxCursor->GetPaM();
        }
        else
        {
            if (pxRange->GetPositions(aPam))
            {
                pPam = & aPam;
            }
        }
        if (!pPam) { return false; }
        bRet = pDoc->getIDocumentContentOperations().CopyRange( *pPam, aPos, /*bCopyAll=*/false, /*bCheckPos=*/true ) || bRet;
    }
 
    pInsDoc->getIDocumentFieldsAccess().UnlockExpFields();
    if( !pInsDoc->getIDocumentFieldsAccess().IsExpFieldsLocked() )
        pInsDoc->getIDocumentFieldsAccess().UpdateExpFields(nullptr, true);
 
    return bRet;
}
 
uno::Reference< text::XAutoTextEntry >  SwXAutoTextGroup::insertNewByName(const OUString& aName,
        const OUString& aTitle, const uno::Reference< text::XTextRange > & xTextRange)
{
    SolarMutexGuard aGuard;
    if(hasByName(aName))
        throw container::ElementExistException();
    if(!xTextRange.is())
        throw uno::RuntimeException();
 
    SwTextBlocks* pGlosGroup = pGlossaries ? pGlossaries->GetGroupDoc(m_sGroupName) : nullptr;
    const OUString& sShortName(aName);
    const OUString& sLongName(aTitle);
    if (pGlosGroup && !pGlosGroup->GetError())
    {
        uno::Reference<lang::XUnoTunnel> xRangeTunnel( xTextRange, uno::UNO_QUERY);
        SwXTextRange* pxRange = nullptr;
        OTextCursorHelper* pxCursor = nullptr;
        if(xRangeTunnel.is())
        {
            pxRange = reinterpret_cast<SwXTextRange*>(xRangeTunnel->getSomething(
                                    SwXTextRange::getUnoTunnelId()));
            pxCursor = reinterpret_cast<OTextCursorHelper*>(xRangeTunnel->getSomething(
                                    OTextCursorHelper::getUnoTunnelId()));
        }
 
        OUString sOnlyText;
        OUString* pOnlyText = nullptr;
        bool bNoAttr = !pxCursor && !pxRange;
        if(bNoAttr)
        {
            sOnlyText = xTextRange->getString();
            pOnlyText = &sOnlyText;
        }
 
        const SvxAutoCorrCfg& rCfg = SvxAutoCorrCfg::Get();
 
        SwDoc* pGDoc = pGlosGroup->GetDoc();
 
        // Until there is an option for that, delete base util::URL
        if(rCfg.IsSaveRelFile())
        {
            INetURLObject aTemp(pGlosGroup->GetFileName());
            pGlosGroup->SetBaseURL( aTemp.GetMainURL(INetURLObject::DecodeMechanism::NONE));
        }
        else
            pGlosGroup->SetBaseURL( OUString() );
 
        sal_uInt16 nRet = USHRT_MAX;
        if( pOnlyText )
            nRet = pGlosGroup->PutText( sShortName, sLongName, *pOnlyText );
        else
        {
            pGlosGroup->ClearDoc();
            if( pGlosGroup->BeginPutDoc( sShortName, sLongName ) )
            {
                pGDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( RedlineFlags::DeleteRedlines );
                lcl_CopySelToDoc( pGDoc, pxCursor, pxRange );
                pGDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern(RedlineFlags::NONE);
                nRet = pGlosGroup->PutDoc();
            }
        }
 
        if (nRet == USHRT_MAX)
        {
            throw uno::RuntimeException();
        }
    }
    delete pGlosGroup;
 
    uno::Reference< text::XAutoTextEntry > xEntry;
 
    try
    {
        xEntry = pGlossaries ?
            pGlossaries->GetAutoTextEntry( m_sGroupName, sName, sShortName ) :
            uno::Reference< text::XAutoTextEntry >();
        OSL_ENSURE( xEntry.is(), "SwXAutoTextGroup::insertNewByName: no UNO object created? How this?" );
            // we just inserted the entry into the group, so why doesn't it exist?
    }
    catch (const container::ElementExistException&)
    {
        throw;
    }
    catch (const uno::RuntimeException&)
    {
        throw;
    }
    catch (const uno::Exception&)
    {
        css::uno::Any anyEx = cppu::getCaughtException();
        throw css::lang::WrappedTargetRuntimeException(
               "Error Getting AutoText!",
               static_cast < OWeakObject * > ( this ),
               anyEx );
    }
 
    return xEntry;
}
 
void SwXAutoTextGroup::removeByName(const OUString& aEntryName)
{
    SolarMutexGuard aGuard;
    std::unique_ptr<SwTextBlocks> pGlosGroup(pGlossaries ? pGlossaries->GetGroupDoc(m_sGroupName) : nullptr);
    if(!pGlosGroup || pGlosGroup->GetError())
        throw container::NoSuchElementException();
 
    sal_uInt16 nIdx = pGlosGroup->GetIndex(aEntryName);
    if ( nIdx != USHRT_MAX )
        pGlosGroup->Delete(nIdx);
}
 
OUString SwXAutoTextGroup::getName()
{
    SolarMutexGuard aGuard;
    return sName;
}
 
void SwXAutoTextGroup::setName(const OUString& rName)
{
    SolarMutexGuard aGuard;
    if( !pGlossaries )
        throw uno::RuntimeException();
 
    sal_Int32 nNewDelimPos = rName.lastIndexOf( GLOS_DELIM );
    sal_Int32 nOldDelimPos = sName.lastIndexOf( GLOS_DELIM );
 
    OUString aNewSuffix;
    if (nNewDelimPos > -1)
        aNewSuffix = rName.copy( nNewDelimPos + 1 );
    OUString aOldSuffix;
    if (nOldDelimPos > -1)
        aOldSuffix = sName.copy( nOldDelimPos + 1 );
 
    sal_Int32 nNewNumeric = aNewSuffix.toInt32();
    sal_Int32 nOldNumeric = aOldSuffix.toInt32();
 
    OUString aNewPrefix( (nNewDelimPos > 1) ? rName.copy( 0, nNewDelimPos ) : rName );
    OUString aOldPrefix( (nOldDelimPos > 1) ? sName.copy( 0, nOldDelimPos ) : sName );
 
    if ( sName == rName ||
       ( nNewNumeric == nOldNumeric && aNewPrefix == aOldPrefix ) )
        return;
    OUString sNewGroup(rName);
    if (sNewGroup.indexOf(GLOS_DELIM)<0)
    {
        sNewGroup += OUStringLiteral1(GLOS_DELIM) + "0";
    }
 
    //the name must be saved, the group may be invalidated while in RenameGroupDoc()
    SwGlossaries* pTempGlossaries = pGlossaries;
 
    OUString sPreserveTitle( pGlossaries->GetGroupTitle( sName ) );
    if ( !pGlossaries->RenameGroupDoc( sName, sNewGroup, sPreserveTitle ) )
        throw uno::RuntimeException();
    sName = rName;
    m_sGroupName = sNewGroup;
    pGlossaries = pTempGlossaries;
}
 
sal_Int32 SwXAutoTextGroup::getCount()
{
    SolarMutexGuard aGuard;
    std::unique_ptr<SwTextBlocks> pGlosGroup(pGlossaries ? pGlossaries->GetGroupDoc(m_sGroupName) : nullptr);
    if (!pGlosGroup || pGlosGroup->GetError())
        throw uno::RuntimeException();
    return static_cast<sal_Int32>(pGlosGroup->GetCount());
}
 
uno::Any SwXAutoTextGroup::getByIndex(sal_Int32 nIndex)
{
    SolarMutexGuard aGuard;
    std::unique_ptr<SwTextBlocks> pGlosGroup(pGlossaries ? pGlossaries->GetGroupDoc(m_sGroupName) : nullptr);
    if (!pGlosGroup || pGlosGroup->GetError())
        throw uno::RuntimeException();
    const sal_uInt16 nCount = pGlosGroup->GetCount();
    if (nIndex < 0 || nIndex >= static_cast<sal_Int32>(nCount))
        throw lang::IndexOutOfBoundsException();
    return getByName(pGlosGroup->GetShortName(static_cast<sal_uInt16>(nIndex)));
}
 
uno::Type SwXAutoTextGroup::getElementType()
{
    return cppu::UnoType<text::XAutoTextEntry>::get();
 
}
 
sal_Bool SwXAutoTextGroup::hasElements()
{
    SolarMutexGuard aGuard;
    std::unique_ptr<SwTextBlocks> pGlosGroup(pGlossaries ? pGlossaries->GetGroupDoc(m_sGroupName) : nullptr);
    if (!pGlosGroup || pGlosGroup->GetError())
        throw uno::RuntimeException();
    return pGlosGroup->GetCount() > 0;
 
}
 
uno::Any SwXAutoTextGroup::getByName(const OUString& _rName)
{
    SolarMutexGuard aGuard;
    uno::Reference< text::XAutoTextEntry > xEntry = pGlossaries->GetAutoTextEntry( m_sGroupName, sName, _rName );
    OSL_ENSURE( xEntry.is(), "SwXAutoTextGroup::getByName: GetAutoTextEntry is fractious!" );
        // we told it to create the object, so why didn't it?
    return makeAny( xEntry );
}
 
uno::Sequence< OUString > SwXAutoTextGroup::getElementNames()
{
    SolarMutexGuard aGuard;
    std::unique_ptr<SwTextBlocks> pGlosGroup(pGlossaries ? pGlossaries->GetGroupDoc(m_sGroupName) : nullptr);
    if (!pGlosGroup || pGlosGroup->GetError())
        throw uno::RuntimeException();
 
    const sal_uInt16 nCount = pGlosGroup->GetCount();
    uno::Sequence< OUString > aEntryNames(nCount);
    OUString *pArr = aEntryNames.getArray();
 
    for ( sal_uInt16 i = 0; i < nCount; i++ )
        pArr[i] = pGlosGroup->GetShortName(i);
    return aEntryNames;
}
 
sal_Bool SwXAutoTextGroup::hasByName(const OUString& rName)
{
    SolarMutexGuard aGuard;
    bool bRet = false;
    std::unique_ptr<SwTextBlocks> pGlosGroup(pGlossaries ? pGlossaries->GetGroupDoc(m_sGroupName) : nullptr);
    if (!pGlosGroup || pGlosGroup->GetError())
        throw uno::RuntimeException();
 
    const sal_uInt16 nCount = pGlosGroup->GetCount();
    for( sal_uInt16 i = 0; i < nCount; ++i )
    {
        OUString sCompare(pGlosGroup->GetShortName(i));
        if(sCompare.equalsIgnoreAsciiCase(rName))
        {
            bRet = true;
            break;
        }
    }
    return bRet;
}
 
uno::Reference< beans::XPropertySetInfo >  SwXAutoTextGroup::getPropertySetInfo()
{
    static uno::Reference< beans::XPropertySetInfo >  xRet = pPropSet->getPropertySetInfo();
    return xRet;
}
 
void SwXAutoTextGroup::setPropertyValue(
    const OUString& rPropertyName, const uno::Any& aValue)
{
    SolarMutexGuard aGuard;
    const SfxItemPropertySimpleEntry*   pEntry = pPropSet->getPropertyMap().getByName( rPropertyName );
 
    if(!pEntry)
        throw beans::UnknownPropertyException();
 
    std::unique_ptr<SwTextBlocks> pGlosGroup(pGlossaries ? pGlossaries->GetGroupDoc(m_sGroupName) : nullptr);
    if(!pGlosGroup || pGlosGroup->GetError())
        throw uno::RuntimeException();
    switch(pEntry->nWID)
    {
        case  WID_GROUP_TITLE:
        {
            OUString sNewTitle;
            aValue >>= sNewTitle;
            if(sNewTitle.isEmpty())
                throw lang::IllegalArgumentException();
            bool bChanged = sNewTitle != pGlosGroup->GetName();
            pGlosGroup->SetName(sNewTitle);
            if(bChanged && HasGlossaryList())
                GetGlossaryList()->ClearGroups();
        }
        break;
    }
}
 
uno::Any SwXAutoTextGroup::getPropertyValue(const OUString& rPropertyName)
{
    SolarMutexGuard aGuard;
    const SfxItemPropertySimpleEntry*   pEntry = pPropSet->getPropertyMap().getByName( rPropertyName);
 
    if(!pEntry)
        throw beans::UnknownPropertyException();
    std::unique_ptr<SwTextBlocks> pGlosGroup(pGlossaries ? pGlossaries->GetGroupDoc(m_sGroupName) : nullptr);
    if(!pGlosGroup  || pGlosGroup->GetError())
        throw uno::RuntimeException();
 
    uno::Any aAny;
    switch(pEntry->nWID)
    {
        case  WID_GROUP_PATH:
            aAny <<= pGlosGroup->GetFileName();
        break;
        case  WID_GROUP_TITLE:
            aAny <<= pGlosGroup->GetName();
        break;
    }
    return aAny;
}
 
void SwXAutoTextGroup::addPropertyChangeListener(
    const OUString& /*PropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*aListener*/)
{
}
 
void SwXAutoTextGroup::removePropertyChangeListener(
    const OUString& /*PropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*aListener*/)
{
}
 
void SwXAutoTextGroup::addVetoableChangeListener(
    const OUString& /*PropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*aListener*/)
{
}
 
void SwXAutoTextGroup::removeVetoableChangeListener(
    const OUString& /*PropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*aListener*/)
{
}
 
void SwXAutoTextGroup::Invalidate()
{
    pGlossaries = nullptr;
    sName.clear();
    m_sGroupName.clear();
}
 
OUString SwXAutoTextGroup::getImplementationName()
{
    return OUString("SwXAutoTextGroup");
}
 
sal_Bool SwXAutoTextGroup::supportsService(const OUString& rServiceName)
{
    return cppu::supportsService(this, rServiceName);
}
 
uno::Sequence< OUString > SwXAutoTextGroup::getSupportedServiceNames()
{
    uno::Sequence<OUString> aRet { "com.sun.star.text.AutoTextGroup" };
    return aRet;
}
 
namespace
{
    class theSwXAutoTextEntryUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXAutoTextEntryUnoTunnelId > {};
}
 
const uno::Sequence< sal_Int8 > & SwXAutoTextEntry::getUnoTunnelId()
{
    return theSwXAutoTextEntryUnoTunnelId::get().getSeq();
}
 
sal_Int64 SAL_CALL SwXAutoTextEntry::getSomething( const uno::Sequence< sal_Int8 >& rId )
{
    if( rId.getLength() == 16
        && 0 == memcmp( getUnoTunnelId().getConstArray(),
                                        rId.getConstArray(), 16 ) )
    {
            return sal::static_int_cast< sal_Int64 >( reinterpret_cast< sal_IntPtr >( this ));
    }
    return 0;
}
 
SwXAutoTextEntry::SwXAutoTextEntry(SwGlossaries* pGlss, const OUString& rGroupName,
                                            const OUString& rEntryName) :
    WeakComponentImplHelper(m_aMutex),
    pGlossaries(pGlss),
    sGroupName(rGroupName),
    sEntryName(rEntryName)
{
}
 
SwXAutoTextEntry::~SwXAutoTextEntry()
{
    SolarMutexGuard aGuard;
 
    // ensure that any pending modifications are written
    implFlushDocument( true );
}
 
void SwXAutoTextEntry::implFlushDocument( bool _bCloseDoc )
{
    if ( xDocSh.is() )
    {
        if ( xDocSh->GetDoc()->getIDocumentState().IsModified () )
            xDocSh->Save();
 
        if ( _bCloseDoc )
        {
            // stop listening at the document
            EndListening( *xDocSh );
 
            xDocSh->DoClose();
            xDocSh.clear();
        }
    }
}
 
void SwXAutoTextEntry::Notify( SfxBroadcaster& _rBC, const SfxHint& _rHint )
{
    if ( &_rBC == xDocSh.get() )
    {   // it's our document
        if (const SfxEventHint* pEventHint = dynamic_cast<const SfxEventHint*>(&_rHint))
        {
            if (SfxEventHintId::PrepareCloseDoc == pEventHint->GetEventId())
            {
                implFlushDocument();
                mxBodyText.clear();
                EndListening( *xDocSh );
                xDocSh.clear();
            }
        }
        else
        {
            if ( SfxHintId::Deinitializing == _rHint.GetId() )
            {
                // our document is dying (possibly because we're shuting down, and the document was notified
                // earlier than we are?)
                // stop listening at the docu
                EndListening( *xDocSh );
                // and release our reference
                xDocSh.clear();
            }
        }
    }
}
 
void SwXAutoTextEntry::GetBodyText ()
{
    SolarMutexGuard aGuard;
 
    xDocSh = pGlossaries->EditGroupDoc ( sGroupName, sEntryName, false );
    OSL_ENSURE( xDocSh.is(), "SwXAutoTextEntry::GetBodyText: unexpected: no doc returned by EditGroupDoc!" );
 
    // start listening at the document
    StartListening( *xDocSh );
 
    mxBodyText = new SwXBodyText ( xDocSh->GetDoc() );
}
 
void SwXAutoTextEntry::disposing()
{
    SolarMutexGuard g;
    implFlushDocument(true);
}
 
uno::Reference< text::XTextCursor >  SwXAutoTextEntry::createTextCursor()
{
    SolarMutexGuard aGuard;
    EnsureBodyText();
    return mxBodyText->createTextCursor();
}
 
uno::Reference< text::XTextCursor >  SwXAutoTextEntry::createTextCursorByRange(
    const uno::Reference< text::XTextRange > & aTextPosition)
{
    SolarMutexGuard aGuard;
    EnsureBodyText();
    return mxBodyText->createTextCursorByRange ( aTextPosition );
}
 
void SwXAutoTextEntry::insertString(const uno::Reference< text::XTextRange > & xRange, const OUString& aString, sal_Bool bAbsorb)
{
    SolarMutexGuard aGuard;
    EnsureBodyText();
    mxBodyText->insertString ( xRange, aString, bAbsorb );
}
 
void SwXAutoTextEntry::insertControlCharacter(const uno::Reference< text::XTextRange > & xRange,
    sal_Int16 nControlCharacter, sal_Bool bAbsorb)
{
    SolarMutexGuard aGuard;
    EnsureBodyText();
    mxBodyText->insertControlCharacter ( xRange, nControlCharacter, bAbsorb );
}
 
void SwXAutoTextEntry::insertTextContent(
    const uno::Reference< text::XTextRange > & xRange,
    const uno::Reference< text::XTextContent > & xContent, sal_Bool bAbsorb)
{
    SolarMutexGuard aGuard;
    EnsureBodyText();
    mxBodyText->insertTextContent ( xRange, xContent, bAbsorb );
}
 
void SwXAutoTextEntry::removeTextContent(
    const uno::Reference< text::XTextContent > & xContent)
{
    SolarMutexGuard aGuard;
    EnsureBodyText();
    mxBodyText->removeTextContent ( xContent );
}
 
uno::Reference< text::XText >  SwXAutoTextEntry::getText()
{
    SolarMutexGuard aGuard;
    uno::Reference< text::XText >  xRet =  static_cast<text::XText*>(this);
    return xRet;
}
 
uno::Reference< text::XTextRange >  SwXAutoTextEntry::getStart()
{
    SolarMutexGuard aGuard;
    EnsureBodyText();
    return mxBodyText->getStart();
}
 
uno::Reference< text::XTextRange >  SwXAutoTextEntry::getEnd()
{
    SolarMutexGuard aGuard;
    EnsureBodyText();
    return mxBodyText->getEnd();
}
 
OUString SwXAutoTextEntry::getString()
{
    SolarMutexGuard aGuard;
    EnsureBodyText();
    return mxBodyText->getString();
}
 
void SwXAutoTextEntry::setString(const OUString& aString)
{
    SolarMutexGuard aGuard;
    EnsureBodyText();
    mxBodyText->setString( aString );
}
 
void SwXAutoTextEntry::applyTo(const uno::Reference< text::XTextRange > & xTextRange)
{
    SolarMutexGuard aGuard;
 
    // ensure that any pending modifications are written
    // reason is that we're holding the _copy_ of the auto text, while the real auto text
    // is stored somewhere. And below, we're not working with our copy, but only tell the target
    // TextRange to work with the stored version.
    // #96380# - 2003-03-03 - fs@openoffice.org
    implFlushDocument();
        // TODO: think about if we should pass "true" here
        // The difference would be that when the next modification is made to this instance here, then
        // we would be forced to open the document again, instead of working on our current copy.
        // This means that we would reflect any changes which were done to the AutoText by foreign instances
        // in the meantime
 
    uno::Reference<lang::XUnoTunnel> xTunnel( xTextRange, uno::UNO_QUERY);
    SwXTextRange* pRange = nullptr;
    OTextCursorHelper* pCursor = nullptr;
    SwXText *pText = nullptr;
 
    if(xTunnel.is())
    {
        pRange = reinterpret_cast < SwXTextRange* >
                ( xTunnel->getSomething( SwXTextRange::getUnoTunnelId() ) );
        pCursor = reinterpret_cast < OTextCursorHelper*>
                ( xTunnel->getSomething( OTextCursorHelper::getUnoTunnelId() ) );
        pText = reinterpret_cast < SwXText* >
                ( xTunnel->getSomething( SwXText::getUnoTunnelId() ) );
    }
 
    SwDoc* pDoc = nullptr;
    if (pRange)
        pDoc = &pRange->GetDoc();
    else if ( pCursor )
        pDoc = pCursor->GetDoc();
    else if ( pText && pText->GetDoc() )
    {
        xTunnel.set(pText->getStart(), uno::UNO_QUERY);
        if (xTunnel.is())
        {
            pCursor = reinterpret_cast < OTextCursorHelper* >
                ( xTunnel->getSomething( OTextCursorHelper::getUnoTunnelId() ) );
            if (pCursor)
                pDoc = pText->GetDoc();
        }
    }
 
    if(!pDoc)
        throw uno::RuntimeException();
 
    SwPaM InsertPaM(pDoc->GetNodes());
    if (pRange)
    {
        if (!pRange->GetPositions(InsertPaM))
        {
            throw uno::RuntimeException();
        }
    }
    else
    {
        InsertPaM = *pCursor->GetPaM();
    }
 
    std::unique_ptr<SwTextBlocks> pBlock(pGlossaries->GetGroupDoc(sGroupName));
    const bool bResult = pBlock.get() && !pBlock->GetError()
                    && pDoc->InsertGlossary( *pBlock, sEntryName, InsertPaM);
 
    if(!bResult)
        throw uno::RuntimeException();
}
 
OUString SwXAutoTextEntry::getImplementationName()
{
    return OUString("SwXAutoTextEntry");
}
 
sal_Bool SwXAutoTextEntry::supportsService(const OUString& rServiceName)
{
    return cppu::supportsService(this, rServiceName);
}
 
uno::Sequence< OUString > SwXAutoTextEntry::getSupportedServiceNames()
{
    uno::Sequence<OUString> aRet { "com.sun.star.text.AutoTextEntry" };
    return aRet;
}
 
uno::Reference< container::XNameReplace > SwXAutoTextEntry::getEvents()
{
    return new SwAutoTextEventDescriptor( *this );
}
 
const struct SvEventDescription aAutotextEvents[] =
{
    { SvMacroItemId::SwStartInsGlossary,  "OnInsertStart" },
    { SvMacroItemId::SwEndInsGlossary,    "OnInsertDone" },
    { SvMacroItemId::NONE, nullptr }
};
 
SwAutoTextEventDescriptor::SwAutoTextEventDescriptor(
    SwXAutoTextEntry& rAutoText ) :
        SvBaseEventDescriptor(aAutotextEvents),
        sSwAutoTextEventDescriptor(
            "SwAutoTextEventDescriptor"),
        rAutoTextEntry(rAutoText)
{
}
 
SwAutoTextEventDescriptor::~SwAutoTextEventDescriptor()
{
}
 
OUString SwAutoTextEventDescriptor::getImplementationName()
{
    return sSwAutoTextEventDescriptor;
}
 
void SwAutoTextEventDescriptor::replaceByName(
    const SvMacroItemId nEvent,
    const SvxMacro& rMacro)
{
    OSL_ENSURE( nullptr != rAutoTextEntry.GetGlossaries(),
                "Strangely enough, the AutoText vanished!" );
    OSL_ENSURE( (nEvent == SvMacroItemId::SwEndInsGlossary) ||
                (nEvent == SvMacroItemId::SwStartInsGlossary) ,
                "Unknown event ID" );
 
    SwGlossaries *const pGlossaries =
        const_cast<SwGlossaries*>(rAutoTextEntry.GetGlossaries());
    std::unique_ptr<SwTextBlocks> pBlocks(
        pGlossaries->GetGroupDoc( rAutoTextEntry.GetGroupName() ));
    OSL_ENSURE( pBlocks,
                "can't get autotext group; SwAutoTextEntry has illegal name?");
 
    if( pBlocks && !pBlocks->GetError())
    {
        sal_uInt16 nIndex = pBlocks->GetIndex( rAutoTextEntry.GetEntryName() );
        if( nIndex != USHRT_MAX )
        {
            SvxMacroTableDtor aMacroTable;
            if( pBlocks->GetMacroTable( nIndex, aMacroTable ) )
            {
                aMacroTable.Insert( nEvent, rMacro );
                pBlocks->SetMacroTable( nIndex, aMacroTable );
            }
        }
    }
    // else: ignore
}
 
void SwAutoTextEventDescriptor::getByName(
    SvxMacro& rMacro,
    const SvMacroItemId nEvent )
{
    OSL_ENSURE( nullptr != rAutoTextEntry.GetGlossaries(), "no AutoText" );
    OSL_ENSURE( (nEvent == SvMacroItemId::SwEndInsGlossary) ||
                (nEvent == SvMacroItemId::SwStartInsGlossary) ,
                "Unknown event ID" );
 
    SwGlossaries *const pGlossaries =
        const_cast<SwGlossaries*>(rAutoTextEntry.GetGlossaries());
    std::unique_ptr<SwTextBlocks> pBlocks(
        pGlossaries->GetGroupDoc( rAutoTextEntry.GetGroupName() ));
    OSL_ENSURE( pBlocks,
                "can't get autotext group; SwAutoTextEntry has illegal name?");
 
    // return empty macro, unless macro is found
    OUString sEmptyStr;
    SvxMacro aEmptyMacro(sEmptyStr, sEmptyStr);
    rMacro = aEmptyMacro;
 
    if ( pBlocks &&  !pBlocks->GetError())
    {
        sal_uInt16 nIndex = pBlocks->GetIndex( rAutoTextEntry.GetEntryName() );
        if( nIndex != USHRT_MAX )
        {
            SvxMacroTableDtor aMacroTable;
            if( pBlocks->GetMacroTable( nIndex, aMacroTable ) )
            {
                SvxMacro *pMacro = aMacroTable.Get( nEvent );
                if( pMacro )
                    rMacro = *pMacro;
            }
        }
    }
}
 
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
SwXAutoTextContainer_get_implementation(css::uno::XComponentContext*,
        css::uno::Sequence<css::uno::Any> const &)
{
    //the module may not be loaded
    SolarMutexGuard aGuard;
    SwGlobals::ensure();
    return cppu::acquire(new SwXAutoTextContainer());
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V560 A part of conditional expression is always true: nIdx != 0xffff.