/* -*- 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 <editeng/forbiddencharacterstable.hxx>
#include <officecfg/Office/Common.hxx>
#include <svx/dialmgr.hxx>
#include <svx/svdetc.hxx>
#include <svx/svdmodel.hxx>
#include <svx/svdtrans.hxx>
#include <svx/strings.hrc>
#include <svx/svdviter.hxx>
#include <svx/svdview.hxx>
#include <svx/svdoutl.hxx>
#include <vcl/bitmapaccess.hxx>
#include <editeng/editdata.hxx>
#include <editeng/eeitem.hxx>
#include <svl/itemset.hxx>
#include <svl/whiter.hxx>
#include <editeng/fontitem.hxx>
#include <editeng/colritem.hxx>
#include <editeng/fhgtitem.hxx>
#include <svx/xgrad.hxx>
#include <svx/xfillit0.hxx>
#include <svx/xflclit.hxx>
#include <svx/xflhtit.hxx>
#include <svx/xbtmpit.hxx>
#include <svx/xflgrit.hxx>
#include <svx/svdoole2.hxx>
#include <svl/itempool.hxx>
#include <unotools/configmgr.hxx>
#include <unotools/localedatawrapper.hxx>
#include <i18nlangtag/lang.h>
#include <unotools/syslocale.hxx>
#include <svx/xflbckit.hxx>
#include <svx/extrusionbar.hxx>
#include <svx/fontworkbar.hxx>
#include <vcl/svapp.hxx>
#include <vcl/settings.hxx>
#include <svx/sdr/contact/viewcontact.hxx>
#include <svx/svdpage.hxx>
#include <svx/svdotable.hxx>
#include <svx/sdrhittesthelper.hxx>

#include <com/sun/star/frame/XModel.hpp>
#include <com/sun/star/embed/XEmbeddedObject.hpp>
#include <com/sun/star/embed/EmbedStates.hpp>
#include <com/sun/star/lang/Locale.hpp>

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

// Global data of the DrawingEngine
SdrGlobalData::SdrGlobalData()
    : pSysLocale(nullptr)
    , pLocaleData(nullptr)
{
    if (!utl::ConfigManager::IsFuzzing())
    {
        svx::ExtrusionBar::RegisterInterface();
        svx::FontworkBar::RegisterInterface();
    }
}

const SvtSysLocale*         SdrGlobalData::GetSysLocale()
{
    if ( !pSysLocale )
        pSysLocale = new SvtSysLocale;
    return pSysLocale;
}
const LocaleDataWrapper*    SdrGlobalData::GetLocaleData()
{
    if ( !pLocaleData )
        pLocaleData = GetSysLocale()->GetLocaleDataPtr();
    return pLocaleData;
}

namespace {

struct TheSdrGlobalData: public rtl::Static<SdrGlobalData, TheSdrGlobalData> {};

}

SdrGlobalData & GetSdrGlobalData() {
    return TheSdrGlobalData::get();
}

OLEObjCache::OLEObjCache()
{
    if (!utl::ConfigManager::IsFuzzing())
        nSize = officecfg::Office::Common::Cache::DrawingEngine::OLE_Objects::get();
    else
        nSize = 100;
    pTimer.reset( new AutoTimer( "svx OLEObjCache pTimer UnloadCheck" ) );
    pTimer->SetInvokeHandler( LINK(this, OLEObjCache, UnloadCheckHdl) );
    pTimer->SetTimeout(20000);
    pTimer->SetStatic();
}

OLEObjCache::~OLEObjCache()
{
    pTimer->Stop();
}

IMPL_LINK_NOARG(OLEObjCache, UnloadCheckHdl, Timer*, void)
{
    if (nSize >= maObjs.size())
        return;

    // more objects than configured cache size try to remove objects
    // of course not the freshly inserted one at nIndex=0
    size_t nCount2 = maObjs.size();
    size_t nIndex = nCount2-1;
    while( nIndex && nCount2 > nSize )
    {
        SdrOle2Obj* pUnloadObj = maObjs[nIndex--];
        if (!pUnloadObj)
            continue;

        try
        {
            // it is important to get object without reinitialization to avoid reentrance
            uno::Reference< embed::XEmbeddedObject > xUnloadObj = pUnloadObj->GetObjRef_NoInit();

            bool bUnload = SdrOle2Obj::CanUnloadRunningObj( xUnloadObj, pUnloadObj->GetAspect() );

            // check whether the object can be unloaded before looking for the parent objects
            if ( xUnloadObj.is() && bUnload )
            {
                uno::Reference< frame::XModel > xUnloadModel( xUnloadObj->getComponent(), uno::UNO_QUERY );
                if ( xUnloadModel.is() )
                {
                    for (SdrOle2Obj* pCacheObj : maObjs)
                    {
                        if ( pCacheObj && pCacheObj != pUnloadObj )
                        {
                            uno::Reference< frame::XModel > xParentModel = pCacheObj->GetParentXModel();
                            if ( xUnloadModel == xParentModel )
                                bUnload = false; // the object has running embedded objects
                        }
                    }
                }
            }

            if ( bUnload && UnloadObj(pUnloadObj) )
                // object was successfully unloaded
                nCount2--;
        }
        catch( uno::Exception& )
        {}
    }
}

void OLEObjCache::InsertObj(SdrOle2Obj* pObj)
{
    if (!maObjs.empty())
    {
        SdrOle2Obj* pExistingObj = maObjs.front();
        if ( pObj == pExistingObj )
            // the object is already on the top, nothing has to be changed
            return;
    }

    // get the old position of the object to know whether it is already in container
    std::vector<SdrOle2Obj*>::iterator it = std::find(maObjs.begin(), maObjs.end(), pObj);
    bool bFound = it != maObjs.end();

    if (bFound)
        maObjs.erase(it);
    // insert object into first position
    maObjs.insert(maObjs.begin(), pObj);

    // if a new object was inserted, recalculate the cache
    if (!bFound)
        pTimer->Invoke();

    if (!bFound || !pTimer->IsActive())
        pTimer->Start();
}

void OLEObjCache::RemoveObj(SdrOle2Obj* pObj)
{
    std::vector<SdrOle2Obj*>::iterator it = std::find(maObjs.begin(), maObjs.end(), pObj);
    if (it != maObjs.end())
        maObjs.erase(it);
    if (maObjs.empty())
        pTimer->Stop();
}

size_t OLEObjCache::size() const
{
    return maObjs.size();
}

SdrOle2Obj* OLEObjCache::operator[](size_t nPos)
{
    return maObjs[nPos];
}

const SdrOle2Obj* OLEObjCache::operator[](size_t nPos) const
{
    return maObjs[nPos];
}

bool OLEObjCache::UnloadObj(SdrOle2Obj* pObj)
{
    bool bUnloaded = false;
    if (pObj)
    {
        //#i80528# The old mechanism is completely useless, only taking into account if
        // in all views the GrafDraft feature is used. This will nearly never have been the
        // case since no one ever used this option.

        // A much better (and working) criteria would be the VOC contact count.
        // The question is what will happen when i make it work now suddenly? I
        // will try it for 2.4.
        const sdr::contact::ViewContact& rViewContact = pObj->GetViewContact();
        const bool bVisible(rViewContact.HasViewObjectContacts());

        if(!bVisible)
        {
            bUnloaded = pObj->Unload();
        }
    }

    return bUnloaded;
}

bool GetDraftFillColor(const SfxItemSet& rSet, Color& rCol)
{
    drawing::FillStyle eFill=rSet.Get(XATTR_FILLSTYLE).GetValue();
    bool bRetval = false;

    switch(eFill)
    {
        case drawing::FillStyle_SOLID:
        {
            rCol = rSet.Get(XATTR_FILLCOLOR).GetColorValue();
            bRetval = true;

            break;
        }
        case drawing::FillStyle_HATCH:
        {
            Color aCol1(rSet.Get(XATTR_FILLHATCH).GetHatchValue().GetColor());
            Color aCol2(COL_WHITE);

            // when hatched background is activated, use object fill color as hatch color
            bool bFillHatchBackground = rSet.Get(XATTR_FILLBACKGROUND).GetValue();
            if(bFillHatchBackground)
            {
                aCol2 = rSet.Get(XATTR_FILLCOLOR).GetColorValue();
            }

            const basegfx::BColor aAverageColor(basegfx::average(aCol1.getBColor(), aCol2.getBColor()));
            rCol = Color(aAverageColor);
            bRetval = true;

            break;
        }
        case drawing::FillStyle_GRADIENT: {
            const XGradient& rGrad=rSet.Get(XATTR_FILLGRADIENT).GetGradientValue();
            Color aCol1(rGrad.GetStartColor());
            Color aCol2(rGrad.GetEndColor());
            const basegfx::BColor aAverageColor(basegfx::average(aCol1.getBColor(), aCol2.getBColor()));
            rCol = Color(aAverageColor);
            bRetval = true;

            break;
        }
        case drawing::FillStyle_BITMAP:
        {
            Bitmap aBitmap(rSet.Get(XATTR_FILLBITMAP).GetGraphicObject().GetGraphic().GetBitmapEx().GetBitmap());
            const Size aSize(aBitmap.GetSizePixel());
            const sal_uInt32 nWidth = aSize.Width();
            const sal_uInt32 nHeight = aSize.Height();
            if (nWidth <= 0 || nHeight <= 0)
                return bRetval;

            Bitmap::ScopedReadAccess pAccess(aBitmap);

            if (pAccess)
            {
                sal_uInt32 nRt(0);
                sal_uInt32 nGn(0);
                sal_uInt32 nBl(0);
                const sal_uInt32 nMaxSteps(8);
                const sal_uInt32 nXStep((nWidth > nMaxSteps) ? nWidth / nMaxSteps : 1);
                const sal_uInt32 nYStep((nHeight > nMaxSteps) ? nHeight / nMaxSteps : 1);
                sal_uInt32 nCount(0);

                for(sal_uInt32 nY(0); nY < nHeight; nY += nYStep)
                {
                    for(sal_uInt32 nX(0); nX < nWidth; nX += nXStep)
                    {
                        const BitmapColor& rCol2 = pAccess->GetColor(nY, nX);

                        nRt += rCol2.GetRed();
                        nGn += rCol2.GetGreen();
                        nBl += rCol2.GetBlue();
                        nCount++;
                    }
                }

                nRt /= nCount;
                nGn /= nCount;
                nBl /= nCount;

                rCol = Color(sal_uInt8(nRt), sal_uInt8(nGn), sal_uInt8(nBl));

                bRetval = true;
            }
            break;
        }
        default: break;
    }

    return bRetval;
}

std::unique_ptr<SdrOutliner> SdrMakeOutliner(OutlinerMode nOutlinerMode, SdrModel& rModel)
{
    SfxItemPool* pPool = &rModel.GetItemPool();
    std::unique_ptr<SdrOutliner> pOutl(new SdrOutliner( pPool, nOutlinerMode ));
    pOutl->SetEditTextObjectPool( pPool );
    pOutl->SetStyleSheetPool( static_cast<SfxStyleSheetPool*>(rModel.GetStyleSheetPool()));
    pOutl->SetDefTab(rModel.GetDefaultTabulator());
    Outliner::SetForbiddenCharsTable(rModel.GetForbiddenCharsTable());
    pOutl->SetAsianCompressionMode(rModel.GetCharCompressType());
    pOutl->SetKernAsianPunctuation(rModel.IsKernAsianPunctuation());
    pOutl->SetAddExtLeading(rModel.IsAddExtLeading());
    return pOutl;
}

std::vector<Link<SdrObjCreatorParams, SdrObject*>>& ImpGetUserMakeObjHdl()
{
    SdrGlobalData& rGlobalData=GetSdrGlobalData();
    return rGlobalData.aUserMakeObjHdl;
}

bool SearchOutlinerItems(const SfxItemSet& rSet, bool bInklDefaults, bool* pbOnlyEE)
{
    bool bHas=false;
    bool bOnly=true;
    bool bLookOnly=pbOnlyEE!=nullptr;
    SfxWhichIter aIter(rSet);
    sal_uInt16 nWhich=aIter.FirstWhich();
    while (((bLookOnly && bOnly) || !bHas) && nWhich!=0) {
        // For bInklDefaults, the entire Which range is decisive,
        // in other cases only the set items are.
        // Disabled and DontCare are regarded as holes in the Which range.
        SfxItemState eState=rSet.GetItemState(nWhich);
        if ((eState==SfxItemState::DEFAULT && bInklDefaults) || eState==SfxItemState::SET) {
            if (nWhich<EE_ITEMS_START || nWhich>EE_ITEMS_END) bOnly=false;
            else bHas=true;
        }
        nWhich=aIter.NextWhich();
    }
    if (!bHas) bOnly=false;
    if (pbOnlyEE!=nullptr) *pbOnlyEE=bOnly;
    return bHas;
}

std::unique_ptr<sal_uInt16[]> RemoveWhichRange(const sal_uInt16* pOldWhichTable, sal_uInt16 nRangeBeg, sal_uInt16 nRangeEnd)
{
    // Six possible cases (per range):
    //         [Beg..End]          Range, to delete
    // [b..e]    [b..e]    [b..e]  Cases 1,3,2: doesn't matter, delete, doesn't matter  + Ranges
    // [b........e]  [b........e]  Cases 4,5  : shrink range                            | in
    // [b......................e]  Case  6    : splitting                               + pOldWhichTable
    sal_uInt16 nCount=0;
    while (pOldWhichTable[nCount]!=0) nCount++;
    nCount++; // nCount should now be an odd number (0 for end of array)
    DBG_ASSERT((nCount&1)==1,"RemoveWhichRange: WhichTable doesn't have an odd number of entries.");
    sal_uInt16 nAlloc=nCount;
    // check necessary size of new array
    sal_uInt16 nNum=nCount-1;
    while (nNum!=0) {
        nNum-=2;
        sal_uInt16 nBeg=pOldWhichTable[nNum];
        sal_uInt16 nEnd=pOldWhichTable[nNum+1];
        if (nEnd<nRangeBeg)  /*nCase=1*/ ;
        else if (nBeg>nRangeEnd) /* nCase=2 */ ;
        else if (nBeg>=nRangeBeg && nEnd<=nRangeEnd) /* nCase=3 */ nAlloc-=2;
        else if (nEnd<=nRangeEnd) /* nCase=4 */;
        else if (nBeg>=nRangeBeg) /* nCase=5*/ ;
        else /* nCase=6 */ nAlloc+=2;
    }

    std::unique_ptr<sal_uInt16[]> pNewWhichTable(new sal_uInt16[nAlloc]);
    memcpy(pNewWhichTable.get(), pOldWhichTable, nAlloc*sizeof(sal_uInt16));
    pNewWhichTable[nAlloc-1]=0; // in case 3, there's no 0 at the end.
    // now remove the unwanted ranges
    nNum=nAlloc-1;
    while (nNum!=0) {
        nNum-=2;
        sal_uInt16 nBeg=pNewWhichTable[nNum];
        sal_uInt16 nEnd=pNewWhichTable[nNum+1];
        unsigned nCase=0;
        if (nEnd<nRangeBeg) nCase=1;
        else if (nBeg>nRangeEnd) nCase=2;
        else if (nBeg>=nRangeBeg && nEnd<=nRangeEnd) nCase=3;
        else if (nEnd<=nRangeEnd) nCase=4;
        else if (nBeg>=nRangeBeg) nCase=5;
        else nCase=6;
        switch (nCase) {
            case 3: {
                unsigned nTailBytes=(nCount-(nNum+2))*sizeof(sal_uInt16);
                memcpy(&pNewWhichTable[nNum],&pNewWhichTable[nNum+2],nTailBytes);
                nCount-=2; // remember: array is now smaller
            } break;
            case 4: pNewWhichTable[nNum+1]=nRangeBeg-1; break;
            case 5: pNewWhichTable[nNum]=nRangeEnd+1;     break;
            case 6: {
                unsigned nTailBytes=(nCount-(nNum+2))*sizeof(sal_uInt16);
                memcpy(&pNewWhichTable[nNum+4],&pNewWhichTable[nNum+2],nTailBytes);
                nCount+=2; // remember:array is now larger
                pNewWhichTable[nNum+2]=nRangeEnd+1;
                pNewWhichTable[nNum+3]=pNewWhichTable[nNum+1];
                pNewWhichTable[nNum+1]=nRangeBeg-1;
            } break;
        } // switch
    }
    return pNewWhichTable;
}


SvdProgressInfo::SvdProgressInfo( const Link<void*,bool>&_rLink )
{
    maLink = _rLink;
    m_nSumCurAction   = 0;

    m_nObjCount = 0;
    m_nCurObj   = 0;

    m_nActionCount = 0;
    m_nCurAction   = 0;

    m_nInsertCount = 0;
    m_nCurInsert   = 0;
}

void SvdProgressInfo::Init( size_t nObjCount )
{
    m_nObjCount = nObjCount;
}

bool SvdProgressInfo::ReportActions( size_t nActionCount )
{
    m_nSumCurAction += nActionCount;
    m_nCurAction += nActionCount;
    if(m_nCurAction > m_nActionCount)
        m_nCurAction = m_nActionCount;

    return maLink.Call(nullptr);
}

void SvdProgressInfo::ReportInserts( size_t nInsertCount )
{
    m_nSumCurAction += nInsertCount;
    m_nCurInsert += nInsertCount;

    maLink.Call(nullptr);
}

void SvdProgressInfo::ReportRescales( size_t nRescaleCount )
{
    m_nSumCurAction += nRescaleCount;
    maLink.Call(nullptr);
}

void SvdProgressInfo::SetActionCount( size_t nActionCount )
{
    m_nActionCount = nActionCount;
}

void SvdProgressInfo::SetInsertCount( size_t nInsertCount )
{
    m_nInsertCount = nInsertCount;
}

void SvdProgressInfo::SetNextObject()
{
    m_nActionCount = 0;
    m_nCurAction   = 0;

    m_nInsertCount = 0;
    m_nCurInsert   = 0;

    m_nCurObj++;
    ReportActions(0);
}

// #i101872# isolate GetTextEditBackgroundColor to tooling; it will anyways only be used as long
// as text edit is not running on overlay

namespace
{
    bool impGetSdrObjListFillColor(
        const SdrObjList& rList,
        const Point& rPnt,
        const SdrPageView& rTextEditPV,
        const SdrLayerIDSet& rVisLayers,
        Color& rCol)
    {
        bool bRet(false);
        bool bMaster(rList.getSdrPageFromSdrObjList() && rList.getSdrPageFromSdrObjList()->IsMasterPage());

        for(size_t no(rList.GetObjCount()); !bRet && no > 0; )
        {
            no--;
            SdrObject* pObj = rList.GetObj(no);
            SdrObjList* pOL = pObj->GetSubList();

            if(pOL)
            {
                // group object
                bRet = impGetSdrObjListFillColor(*pOL, rPnt, rTextEditPV, rVisLayers, rCol);
            }
            else
            {
                SdrTextObj* pText = dynamic_cast< SdrTextObj * >(pObj);

                // Exclude zero master page object (i.e. background shape) from color query
                if(pText
                    && pObj->IsClosedObj()
                    && (!bMaster || (!pObj->IsNotVisibleAsMaster() && 0 != no))
                    && pObj->GetCurrentBoundRect().IsInside(rPnt)
                    && !pText->IsHideContour()
                    && SdrObjectPrimitiveHit(*pObj, rPnt, 0, rTextEditPV, &rVisLayers, false))
                {
                    bRet = GetDraftFillColor(pObj->GetMergedItemSet(), rCol);
                }
            }
        }

        return bRet;
    }

    bool impGetSdrPageFillColor(
        const SdrPage& rPage,
        const Point& rPnt,
        const SdrPageView& rTextEditPV,
        const SdrLayerIDSet& rVisLayers,
        Color& rCol,
        bool bSkipBackgroundShape)
    {
        bool bRet(impGetSdrObjListFillColor(rPage, rPnt, rTextEditPV, rVisLayers, rCol));

        if(!bRet && !rPage.IsMasterPage())
        {
            if(rPage.TRG_HasMasterPage())
            {
                SdrLayerIDSet aSet(rVisLayers);
                aSet &= rPage.TRG_GetMasterPageVisibleLayers();
                SdrPage& rMasterPage = rPage.TRG_GetMasterPage();

                // Don't fall back to background shape on
                // master pages. This is later handled by
                // GetBackgroundColor, and is necessary to cater for
                // the silly ordering: 1. shapes, 2. master page
                // shapes, 3. page background, 4. master page
                // background.
                bRet = impGetSdrPageFillColor(rMasterPage, rPnt, rTextEditPV, aSet, rCol, true);
            }
        }

        // Only now determine background color from background shapes
        if(!bRet && !bSkipBackgroundShape)
        {
            rCol = rPage.GetPageBackgroundColor();
            return true;
        }

        return bRet;
    }

    Color impCalcBackgroundColor(
        const tools::Rectangle& rArea,
        const SdrPageView& rTextEditPV,
        const SdrPage& rPage)
    {
        svtools::ColorConfig aColorConfig;
        Color aBackground(aColorConfig.GetColorValue(svtools::DOCCOLOR).nColor);
        const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();

        if(!rStyleSettings.GetHighContrastMode())
        {
            // search in page
            const sal_uInt16 SPOTCOUNT(5);
            Point aSpotPos[SPOTCOUNT];
            Color aSpotColor[SPOTCOUNT];
            sal_uInt32 nHeight( rArea.GetSize().Height() );
            sal_uInt32 nWidth( rArea.GetSize().Width() );
            sal_uInt32 nWidth14  = nWidth / 4;
            sal_uInt32 nHeight14 = nHeight / 4;
            sal_uInt32 nWidth34  = ( 3 * nWidth ) / 4;
            sal_uInt32 nHeight34 = ( 3 * nHeight ) / 4;

            sal_uInt16 i;
            for ( i = 0; i < SPOTCOUNT; i++ )
            {
                // five spots are used
                switch ( i )
                {
                    case 0 :
                    {
                        // Center-Spot
                        aSpotPos[i] = rArea.Center();
                    }
                    break;

                    case 1 :
                    {
                        // TopLeft-Spot
                        aSpotPos[i] = rArea.TopLeft();
                        aSpotPos[i].AdjustX(nWidth14 );
                        aSpotPos[i].AdjustY(nHeight14 );
                    }
                    break;

                    case 2 :
                    {
                        // TopRight-Spot
                        aSpotPos[i] = rArea.TopLeft();
                        aSpotPos[i].AdjustX(nWidth34 );
                        aSpotPos[i].AdjustY(nHeight14 );
                    }
                    break;

                    case 3 :
                    {
                        // BottomLeft-Spot
                        aSpotPos[i] = rArea.TopLeft();
                        aSpotPos[i].AdjustX(nWidth14 );
                        aSpotPos[i].AdjustY(nHeight34 );
                    }
                    break;

                    case 4 :
                    {
                        // BottomRight-Spot
                        aSpotPos[i] = rArea.TopLeft();
                        aSpotPos[i].AdjustX(nWidth34 );
                        aSpotPos[i].AdjustY(nHeight34 );
                    }
                    break;

                }

                aSpotColor[i] = COL_WHITE;
                impGetSdrPageFillColor(rPage, aSpotPos[i], rTextEditPV, rTextEditPV.GetVisibleLayers(), aSpotColor[i], false);
            }

            sal_uInt16 aMatch[SPOTCOUNT];

            for ( i = 0; i < SPOTCOUNT; i++ )
            {
                // were same spot colors found?
                aMatch[i] = 0;

                for ( sal_uInt16 j = 0; j < SPOTCOUNT; j++ )
                {
                    if( j != i )
                    {
                        if( aSpotColor[i] == aSpotColor[j] )
                        {
                            aMatch[i]++;
                        }
                    }
                }
            }

            // highest weight to center spot
            aBackground = aSpotColor[0];

            for ( sal_uInt16 nMatchCount = SPOTCOUNT - 1; nMatchCount > 1; nMatchCount-- )
            {
                // which spot color was found most?
                for ( i = 0; i < SPOTCOUNT; i++ )
                {
                    if( aMatch[i] == nMatchCount )
                    {
                        aBackground = aSpotColor[i];
                        nMatchCount = 1;   // break outer for-loop
                        break;
                    }
                }
            }
        }

        return aBackground;
    }
} // end of anonymous namespace

Color GetTextEditBackgroundColor(const SdrObjEditView& rView)
{
    svtools::ColorConfig aColorConfig;
    Color aBackground(aColorConfig.GetColorValue(svtools::DOCCOLOR).nColor);
    const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();

    if(!rStyleSettings.GetHighContrastMode())
    {
        bool bFound(false);
        SdrTextObj* pText = rView.GetTextEditObject();

        if(pText && pText->IsClosedObj())
        {
            sdr::table::SdrTableObj* pTable = dynamic_cast< sdr::table::SdrTableObj * >( pText );

            if( pTable )
                bFound = GetDraftFillColor(pTable->GetActiveCellItemSet(), aBackground );

            if( !bFound )
                bFound=GetDraftFillColor(pText->GetMergedItemSet(), aBackground);
        }

        if(!bFound && pText)
        {
            SdrPageView* pTextEditPV = rView.GetTextEditPageView();

            if(pTextEditPV)
            {
                Point aPvOfs(pText->GetTextEditOffset());
                const SdrPage* pPg = pTextEditPV->GetPage();

                if(pPg)
                {
                    tools::Rectangle aSnapRect( pText->GetSnapRect() );
                    aSnapRect.Move(aPvOfs.X(), aPvOfs.Y());

                    return impCalcBackgroundColor(aSnapRect, *pTextEditPV, *pPg);
                }
            }
        }
    }

    return aBackground;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
