/**************************************************************
 * 
 * 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_svx.hxx"
#include "editeng/forbiddencharacterstable.hxx"
#include <com/sun/star/embed/XEmbeddedObject.hpp>
#include <com/sun/star/embed/EmbedStates.hpp>
#include <svx/svdetc.hxx>
#include "svx/svditext.hxx"
#include <svx/svdmodel.hxx>
#include <svx/svdtrans.hxx>
#include "svx/svdglob.hxx"
#include "svx/svdstr.hrc"
#include "svx/svdviter.hxx"
#include <svx/svdview.hxx>
#include <svx/svdoutl.hxx>
#include <vcl/bmpacc.hxx>
#include <editeng/eeitem.hxx>
#include <svl/itemset.hxx>
#include <tools/config.hxx>
#include <unotools/cacheoptions.hxx>
#include <svl/whiter.hxx>
#include <tools/bigint.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/localedatawrapper.hxx>
#include <com/sun/star/lang/Locale.hpp>
#include <comphelper/processfactory.hxx>
#include <i18npool/lang.h>
#include <unotools/charclass.hxx>
#include <unotools/syslocale.hxx>
#include <svx/xflbckit.hxx>
#include <svx/extrusionbar.hxx>
#include <svx/fontworkbar.hxx>
#include <vcl/svapp.hxx> //add CHINA001
#include <svx/sdr/contact/viewcontact.hxx>
#include <svx/svdpage.hxx>
#include <svx/svdotable.hxx>
#include <svx/sdrhittesthelper.hxx>

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

/******************************************************************************
* Globale Daten der DrawingEngine
******************************************************************************/

SdrGlobalData::SdrGlobalData() :
    pSysLocale(NULL),
    pCharClass(NULL),
    pLocaleData(NULL),
	pOutliner(NULL),
	pDefaults(NULL),
	pResMgr(NULL),
	nExchangeFormat(0)
{
    //pSysLocale = new SvtSysLocale;
    //pCharClass = pSysLocale->GetCharClassPtr();
    //pLocaleData = pSysLocale->GetLocaleDataPtr();

	svx::ExtrusionBar::RegisterInterface();
	svx::FontworkBar::RegisterInterface();
}

SdrGlobalData::~SdrGlobalData()
{
	delete pOutliner;
	delete pDefaults;
	delete pResMgr;
    //! do NOT delete pCharClass and pLocaleData
    delete pSysLocale;
}
const SvtSysLocale*         SdrGlobalData::GetSysLocale()
{
    if ( !pSysLocale )
        pSysLocale = new SvtSysLocale;
    return pSysLocale;
}
const CharClass*            SdrGlobalData::GetCharClass()
{
    if ( !pCharClass )
        pCharClass = GetSysLocale()->GetCharClassPtr();
    return pCharClass;
}
const LocaleDataWrapper*    SdrGlobalData::GetLocaleData()
{
    if ( !pLocaleData )
        pLocaleData = GetSysLocale()->GetLocaleDataPtr();
    return pLocaleData;
}
////////////////////////////////////////////////////////////////////////////////////////////////////

OLEObjCache::OLEObjCache()
:	Container( 0 )
{
	SvtCacheOptions aCacheOptions;

	nSize = aCacheOptions.GetDrawingEngineOLE_Objects();
	pTimer = new AutoTimer();
    Link aLink = LINK(this, OLEObjCache, UnloadCheckHdl);

    pTimer->SetTimeoutHdl(aLink);
    pTimer->SetTimeout(20000);
    pTimer->Start();

	aLink.Call(pTimer);
}

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

void OLEObjCache::UnloadOnDemand()
{
    if ( nSize < Count() )
	{
        // more objects than configured cache size try to remove objects
        // of course not the freshly inserted one at nIndex=0
        sal_uIntPtr nCount2 = Count();
        sal_uIntPtr nIndex = nCount2-1;
        while( nIndex && nCount2 > nSize )
		{
            SdrOle2Obj* pUnloadObj = (SdrOle2Obj*) GetObject(nIndex--);
            if ( pUnloadObj )
            {
                try
                {
                    // it is important to get object without reinitialization to avoid reentrance
                    uno::Reference< embed::XEmbeddedObject > xUnloadObj = pUnloadObj->GetObjRef_NoInit();

                    sal_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 ( sal_uIntPtr nCheckInd = 0; nCheckInd < Count(); nCheckInd++ )
                            {
                                SdrOle2Obj* pCacheObj = (SdrOle2Obj*) GetObject(nCheckInd);
                                if ( pCacheObj && pCacheObj != pUnloadObj )
                                {
                                    uno::Reference< frame::XModel > xParentModel = pCacheObj->GetParentXModel();
                                    if ( xUnloadModel == xParentModel )
                                        bUnload = sal_False; // the object has running embedded objects
                                }
                            }
                        }
                    }

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

void OLEObjCache::SetSize(sal_uIntPtr nNewSize)
{
	nSize = nNewSize;
}

void OLEObjCache::InsertObj(SdrOle2Obj* pObj)
{
	if ( Count() )
	{
		SdrOle2Obj* pExistingObj = (SdrOle2Obj*)GetObject( 0 );
		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
    sal_uIntPtr nOldPos = GetPos( pObj );

    // insert object into first position
	Remove( nOldPos );
	Insert(pObj, (sal_uIntPtr) 0L);

    if ( nOldPos == CONTAINER_ENTRY_NOTFOUND )
    {
        // a new object was inserted, recalculate the cache
        UnloadOnDemand();
    }
}

void OLEObjCache::RemoveObj(SdrOle2Obj* pObj)
{
    Remove(pObj);
}

sal_Bool OLEObjCache::UnloadObj(SdrOle2Obj* pObj)
{
	sal_Bool bUnloaded = sal_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 quesion is what will happen whe 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(true));

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

	return bUnloaded;
}

IMPL_LINK(OLEObjCache, UnloadCheckHdl, AutoTimer*, /*pTim*/)
{
    UnloadOnDemand();
    return 0;
}

void ContainerSorter::DoSort(sal_uIntPtr a, sal_uIntPtr b) const
{
	sal_uIntPtr nAnz=rCont.Count();
	if (b>nAnz) b=nAnz;
	if (b>0) b--;
	if (a<b) ImpSubSort(a,b);
}

void ContainerSorter::Is1stLessThan2nd(const void* /*pElem1*/, const void* /*pElem2*/) const
{
}

void ContainerSorter::ImpSubSort(long nL, long nR) const
{
	long i,j;
	const void* pX;
	void* pI;
	void* pJ;
	i=nL;
	j=nR;
	pX=rCont.GetObject((nL+nR)/2);
	do {
		pI=rCont.Seek(i);
		while (pI!=pX && Compare(pI,pX)<0) { i++; pI=rCont.Next(); }
		pJ=rCont.Seek(j);
		while (pJ!=pX && Compare(pX,pJ)<0) { j--; pJ=rCont.Prev(); }
		if (i<=j) {
			rCont.Replace(pJ,i);
			rCont.Replace(pI,j);
			i++;
			j--;
		}
	} while (i<=j);
	if (nL<j) ImpSubSort(nL,j);
	if (i<nR) ImpSubSort(i,nR);
}

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

class ImpUShortContainerSorter: public ContainerSorter {
public:
	ImpUShortContainerSorter(Container& rNewCont): ContainerSorter(rNewCont) {}
	virtual int Compare(const void* pElem1, const void* pElem2) const;
};

int ImpUShortContainerSorter::Compare(const void* pElem1, const void* pElem2) const
{
	sal_uInt16 n1=sal_uInt16(sal_uIntPtr(pElem1));
	sal_uInt16 n2=sal_uInt16(sal_uIntPtr(pElem2));
	return n1<n2 ? -1 : n1>n2 ? 1 : 0;
}

void UShortCont::Sort()
{
	ImpUShortContainerSorter aSorter(aArr);
	aSorter.DoSort();
}

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

class ImpClipMerk {
	Region aClip;
	FASTBOOL   bClip;
public:
	ImpClipMerk(const OutputDevice& rOut): aClip(rOut.GetClipRegion()),bClip(rOut.IsClipRegion()) {}
	void Restore(OutputDevice& rOut)
	{
		// Kein Clipping in die Metafileaufzeichnung
		GDIMetaFile* pMtf=rOut.GetConnectMetaFile();
		if (pMtf!=NULL && (!pMtf->IsRecord() || pMtf->IsPause())) pMtf=NULL;
		if (pMtf!=NULL) pMtf->Pause(sal_True);
		if (bClip) rOut.SetClipRegion(aClip);
		else rOut.SetClipRegion();
		if (pMtf!=NULL) pMtf->Pause(sal_False);
	}
};

class ImpColorMerk {
	Color aLineColor;
	Color aFillColor;
	Color aBckgrdColor;
	Font  aFont;
public:
	ImpColorMerk(const OutputDevice& rOut):
		aLineColor( rOut.GetLineColor() ),
		aFillColor( rOut.GetFillColor() ),
		aBckgrdColor( rOut.GetBackground().GetColor() ),
		aFont (rOut.GetFont()) {}

	ImpColorMerk(const OutputDevice& rOut, sal_uInt16 nMode)
	{
		if ( (nMode & SDRHDC_SAVEPEN) == SDRHDC_SAVEPEN )
			aLineColor = rOut.GetLineColor();

		if ( (nMode & SDRHDC_SAVEBRUSH) == SDRHDC_SAVEBRUSH)
		{
			aFillColor = rOut.GetFillColor();
			aBckgrdColor = rOut.GetBackground().GetColor();
		}

		if ( (nMode & SDRHDC_SAVEFONT) == SDRHDC_SAVEFONT)
			aFont=rOut.GetFont();
	}

	void Restore(OutputDevice& rOut, sal_uInt16 nMode=SDRHDC_SAVEPENANDBRUSHANDFONT)
	{
		if ( (nMode & SDRHDC_SAVEPEN) == SDRHDC_SAVEPEN)
			rOut.SetLineColor( aLineColor );

		if ( (nMode & SDRHDC_SAVEBRUSH) == SDRHDC_SAVEBRUSH)
		{
			rOut.SetFillColor( aFillColor );
			rOut.SetBackground( Wallpaper( aBckgrdColor ) );
		}
		if ((nMode & SDRHDC_SAVEFONT) ==SDRHDC_SAVEFONT)
		{
			if (!rOut.GetFont().IsSameInstance(aFont))
			{
				rOut.SetFont(aFont);
			}
		}
	}

	const Color& GetLineColor() const { return aLineColor; }
};

ImpSdrHdcMerk::ImpSdrHdcMerk(const OutputDevice& rOut, sal_uInt16 nNewMode, FASTBOOL bAutoMerk):
	pFarbMerk(NULL),
	pClipMerk(NULL),
	pLineColorMerk(NULL),
	nMode(nNewMode)
{
	if (bAutoMerk) Save(rOut);
}

ImpSdrHdcMerk::~ImpSdrHdcMerk()
{
	if (pFarbMerk!=NULL) delete pFarbMerk;
	if (pClipMerk!=NULL) delete pClipMerk;
	if (pLineColorMerk !=NULL) delete pLineColorMerk;
}

void ImpSdrHdcMerk::Save(const OutputDevice& rOut)
{
	if (pFarbMerk!=NULL)
	{
		delete pFarbMerk;
		pFarbMerk=NULL;
	}
	if (pClipMerk!=NULL)
	{
		delete pClipMerk;
		pClipMerk=NULL;
	}
	if (pLineColorMerk !=NULL)
	{
		delete pLineColorMerk ;
		pLineColorMerk =NULL;
	}
	if ((nMode & SDRHDC_SAVECLIPPING) ==SDRHDC_SAVECLIPPING)
		pClipMerk=new ImpClipMerk(rOut);

	sal_uInt16 nCol=nMode & SDRHDC_SAVEPENANDBRUSHANDFONT;

	if (nCol==SDRHDC_SAVEPEN)
		pLineColorMerk=new Color( rOut.GetLineColor() );
	else if (nCol==SDRHDC_SAVEPENANDBRUSHANDFONT)
		pFarbMerk=new ImpColorMerk(rOut);
	else if (nCol!=0)
		pFarbMerk=new ImpColorMerk(rOut,nCol);
}

void ImpSdrHdcMerk::Restore(OutputDevice& rOut, sal_uInt16 nMask) const
{
	nMask&=nMode; // nur restaurieren, was auch gesichert wurde

	if ((nMask & SDRHDC_SAVECLIPPING) ==SDRHDC_SAVECLIPPING && pClipMerk!=NULL)
		pClipMerk->Restore(rOut);

	sal_uInt16 nCol=nMask & SDRHDC_SAVEPENANDBRUSHANDFONT;

	if (nCol==SDRHDC_SAVEPEN)
	{
		if (pLineColorMerk!=NULL)
			rOut.SetLineColor(*pLineColorMerk);
		else if (pFarbMerk!=NULL)
			rOut.SetLineColor( pFarbMerk->GetLineColor() );
	} else if (nCol!=0 && pFarbMerk!=NULL)
		pFarbMerk->Restore(rOut,nCol);
}

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

void SdrLinkList::Clear()
{
	unsigned nAnz=GetLinkCount();
	for (unsigned i=0; i<nAnz; i++) {
		delete (Link*)aList.GetObject(i);
	}
	aList.Clear();
}

unsigned SdrLinkList::FindEntry(const Link& rLink) const
{
	unsigned nAnz=GetLinkCount();
	for (unsigned i=0; i<nAnz; i++) {
		if (GetLink(i)==rLink) return i;
	}
	return 0xFFFF;
}

void SdrLinkList::InsertLink(const Link& rLink, unsigned nPos)
{
	unsigned nFnd=FindEntry(rLink);
	if (nFnd==0xFFFF) {
		if (rLink.IsSet()) {
			aList.Insert(new Link(rLink),nPos);
		} else {
			DBG_ERROR("SdrLinkList::InsertLink(): Versuch, einen nicht gesetzten Link einzufuegen");
		}
	} else {
		DBG_ERROR("SdrLinkList::InsertLink(): Link schon vorhanden");
	}
}

void SdrLinkList::RemoveLink(const Link& rLink)
{
	unsigned nFnd=FindEntry(rLink);
	if (nFnd!=0xFFFF) {
		Link* pLink=(Link*)aList.Remove(nFnd);
		delete pLink;
	} else {
		DBG_ERROR("SdrLinkList::RemoveLink(): Link nicht gefunden");
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// #98988# Re-implement GetDraftFillColor(...)

FASTBOOL GetDraftFillColor(const SfxItemSet& rSet, Color& rCol)
{
	XFillStyle eFill=((XFillStyleItem&)rSet.Get(XATTR_FILLSTYLE)).GetValue();
	FASTBOOL bRetval(sal_False);

	switch(eFill)
	{
		case XFILL_SOLID:
		{
			rCol = ((XFillColorItem&)rSet.Get(XATTR_FILLCOLOR)).GetColorValue();
			bRetval = sal_True;

			break;
		}
		case XFILL_HATCH:
		{
			Color aCol1(((XFillHatchItem&)rSet.Get(XATTR_FILLHATCH)).GetHatchValue().GetColor());
			Color aCol2(COL_WHITE);

			// #97870# when hatch background is activated, use object fill color as hatch color
			sal_Bool bFillHatchBackground = ((const XFillBackgroundItem&)(rSet.Get(XATTR_FILLBACKGROUND))).GetValue();
			if(bFillHatchBackground)
			{
				aCol2 = ((const XFillColorItem&)(rSet.Get(XATTR_FILLCOLOR))).GetColorValue();
			}

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

			break;
		}
		case XFILL_GRADIENT: {
			const XGradient& rGrad=((XFillGradientItem&)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 = sal_True;

			break;
		}
		case XFILL_BITMAP:
		{
			Bitmap aBitmap(((XFillBitmapItem&)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();
			BitmapReadAccess* pAccess = aBitmap.AcquireReadAccess();

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

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

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

				nRt /= nAnz;
				nGn /= nAnz;
				nBl /= nAnz;

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

				bRetval = sal_True;
			}

			if(pAccess)
			{
				aBitmap.ReleaseAccess(pAccess);
			}

			break;
		}
		default: break;
	}

	return bRetval;
}

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

SdrEngineDefaults::SdrEngineDefaults():
	aFontName( OutputDevice::GetDefaultFont( DEFAULTFONT_SERIF, LANGUAGE_SYSTEM, DEFAULTFONT_FLAGS_ONLYONE ).GetName() ),
	eFontFamily(FAMILY_ROMAN),
	aFontColor(COL_AUTO),
	nFontHeight(847),			  // 847/100mm = ca. 24 Point
	eMapUnit(MAP_100TH_MM),
	aMapFraction(1,1)
{
}

SdrEngineDefaults& SdrEngineDefaults::GetDefaults()
{
	SdrGlobalData& rGlobalData=GetSdrGlobalData();
	if (rGlobalData.pDefaults==NULL) {
		rGlobalData.pDefaults=new SdrEngineDefaults;
	}
	return *rGlobalData.pDefaults;
}

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

void SdrEngineDefaults::LanguageHasChanged()
{
	SdrGlobalData& rGlobalData=GetSdrGlobalData();
	if (rGlobalData.pResMgr!=NULL) {
		delete rGlobalData.pResMgr;
		rGlobalData.pResMgr=NULL;
	}
}

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

SdrOutliner* SdrMakeOutliner( sal_uInt16 nOutlinerMode, SdrModel* pModel )
{
	//SdrEngineDefaults& rDefaults = SdrEngineDefaults::GetDefaults();

	SfxItemPool* pPool = &pModel->GetItemPool();
	SdrOutliner* pOutl = new SdrOutliner( pPool, nOutlinerMode );
	pOutl->SetEditTextObjectPool( pPool );
	pOutl->SetStyleSheetPool( (SfxStyleSheetPool*) pModel->GetStyleSheetPool() );
	pOutl->SetDefTab( pModel->GetDefaultTabulator() );
	pOutl->SetForbiddenCharsTable( pModel->GetForbiddenCharsTable() );
	pOutl->SetAsianCompressionMode( pModel->GetCharCompressType() );
	pOutl->SetKernAsianPunctuation( pModel->IsKernAsianPunctuation() );
    pOutl->SetAddExtLeading( pModel->IsAddExtLeading() );

	return pOutl;
}

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


SdrLinkList& ImpGetUserMakeObjHdl()
{
	SdrGlobalData& rGlobalData=GetSdrGlobalData();
	return rGlobalData.aUserMakeObjHdl;
}

SdrLinkList& ImpGetUserMakeObjUserDataHdl()
{
	SdrGlobalData& rGlobalData=GetSdrGlobalData();
	return rGlobalData.aUserMakeObjUserDataHdl;
}

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

ResMgr* ImpGetResMgr()
{
	SdrGlobalData& rGlobalData = GetSdrGlobalData();

	if(!rGlobalData.pResMgr)
	{
		ByteString aName("svx");
		rGlobalData.pResMgr =
            ResMgr::CreateResMgr( aName.GetBuffer(), Application::GetSettings().GetUILocale() );
	}

	return rGlobalData.pResMgr;
}

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

String ImpGetResStr(sal_uInt16 nResID)
{
	return String(ResId(nResID, *ImpGetResMgr()));
}

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

namespace sdr
{
String GetResourceString(sal_uInt16 nResID)
{
	return ImpGetResStr( nResID );
}
}

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

sal_Bool SearchOutlinerItems(const SfxItemSet& rSet, sal_Bool bInklDefaults, sal_Bool* pbOnlyEE)
{
	sal_Bool bHas=sal_False;
	sal_Bool bOnly=sal_True;
	sal_Bool bLookOnly=pbOnlyEE!=NULL;
	SfxWhichIter aIter(rSet);
	sal_uInt16 nWhich=aIter.FirstWhich();
	while (((bLookOnly && bOnly) || !bHas) && nWhich!=0) {
		// bei bInklDefaults ist der gesamte Which-Range
		// ausschlaggebend, ansonsten nur die gesetzten Items
		// Disabled und DontCare wird als Loch im Which-Range betrachtet
		SfxItemState eState=rSet.GetItemState(nWhich);
		if ((eState==SFX_ITEM_DEFAULT && bInklDefaults) || eState==SFX_ITEM_SET) {
			if (nWhich<EE_ITEMS_START || nWhich>EE_ITEMS_END) bOnly=sal_False;
			else bHas=sal_True;
		}
		nWhich=aIter.NextWhich();
	}
	if (!bHas) bOnly=sal_False;
	if (pbOnlyEE!=NULL) *pbOnlyEE=bOnly;
	return bHas;
}

sal_uInt16* RemoveWhichRange(const sal_uInt16* pOldWhichTable, sal_uInt16 nRangeBeg, sal_uInt16 nRangeEnd)
{
	// insgesamt sind 6 Faelle moeglich (je Range):
	//		   [Beg..End]		   zu entfernender Range
	// [b..e]	 [b..e]    [b..e]  Fall 1,3,2: egal, ganz weg, egal  + Ranges
	// [b........e]  [b........e]  Fall 4,5  : Bereich verkleinern	 | in
	// [b......................e]  Fall 6	 : Splitting			 + pOldWhichTable
	sal_uInt16 nAnz=0;
	while (pOldWhichTable[nAnz]!=0) nAnz++;
	nAnz++; // nAnz muesste nun in jedem Fall eine ungerade Zahl sein (0 am Ende des Arrays)
	DBG_ASSERT((nAnz&1)==1,"Joe: RemoveWhichRange: WhichTable hat keine ungerade Anzahl von Eintraegen");
	sal_uInt16 nAlloc=nAnz;
	// benoetigte Groesse des neuen Arrays ermitteln
	sal_uInt16 nNum=nAnz-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;
	}

	sal_uInt16* pNewWhichTable=new sal_uInt16[nAlloc];
	memcpy(pNewWhichTable,pOldWhichTable,nAlloc*sizeof(sal_uInt16));
	pNewWhichTable[nAlloc-1]=0; // im Falle 3 fehlt die 0 am Ende
	// nun die unerwuenschten Ranges entfernen
	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=(nAnz-(nNum+2))*sizeof(sal_uInt16);
				memcpy(&pNewWhichTable[nNum],&pNewWhichTable[nNum+2],nTailBytes);
				nAnz-=2; // Merken: Array hat sich verkleinert
			} break;
			case 4: pNewWhichTable[nNum+1]=nRangeBeg-1; break;
			case 5: pNewWhichTable[nNum]=nRangeEnd+1;	  break;
			case 6: {
				unsigned nTailBytes=(nAnz-(nNum+2))*sizeof(sal_uInt16);
				memcpy(&pNewWhichTable[nNum+4],&pNewWhichTable[nNum+2],nTailBytes);
				nAnz+=2; // Merken: Array hat sich vergroessert
				pNewWhichTable[nNum+2]=nRangeEnd+1;
				pNewWhichTable[nNum+3]=pNewWhichTable[nNum+1];
				pNewWhichTable[nNum+1]=nRangeBeg-1;
			} break;
		} // switch
	}
	return pNewWhichTable;
}

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

SvdProgressInfo::SvdProgressInfo( Link *_pLink )
{
	DBG_ASSERT(_pLink!=NULL,"SvdProgressInfo(): Kein Link angegeben!!");

	pLink = _pLink;
	nSumActionCount = 0;
	nSumCurAction	= 0;

	nObjCount = 0;
	nCurObj   = 0;

	nActionCount = 0;
	nCurAction	 = 0;

	nInsertCount = 0;
	nCurInsert	 = 0;
}

void SvdProgressInfo::Init( sal_uIntPtr _nSumActionCount, sal_uIntPtr _nObjCount )
{
	nSumActionCount = _nSumActionCount;
	nObjCount = _nObjCount;
}

sal_Bool SvdProgressInfo::ReportActions( sal_uIntPtr nAnzActions )
{
	nSumCurAction += nAnzActions;
	nCurAction += nAnzActions;
	if(nCurAction > nActionCount)
		nCurAction = nActionCount;

	return pLink->Call(NULL) == 1L;
}

sal_Bool SvdProgressInfo::ReportInserts( sal_uIntPtr nAnzInserts )
{
	nSumCurAction += nAnzInserts;
	nCurInsert += nAnzInserts;

	return pLink->Call(NULL) == 1L;
}

sal_Bool SvdProgressInfo::ReportRescales( sal_uIntPtr nAnzRescales )
{
	nSumCurAction += nAnzRescales;
	return pLink->Call(NULL) == 1L;
}

void SvdProgressInfo::SetActionCount( sal_uIntPtr _nActionCount )
{
	nActionCount = _nActionCount;
}

void SvdProgressInfo::SetInsertCount( sal_uIntPtr _nInsertCount )
{
	nInsertCount = _nInsertCount;
}

sal_Bool SvdProgressInfo::SetNextObject()
{
	nActionCount = 0;
	nCurAction	 = 0;

	nInsertCount = 0;
	nCurInsert	 = 0;

	nCurObj++;
	return ReportActions(0);
}

void SvdProgressInfo::ReportError()
{
	pLink->Call((void *)1L);
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// #i101872# isolate GetTextEditBackgroundColor to tooling; it woll 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 SetOfByte& rVisLayers,
        Color& rCol)
    {
	    if(!rList.GetModel())
            return false;

	    bool bRet(false);
	    bool bMaster(rList.GetPage() ? rList.GetPage()->IsMasterPage() : false);

	    for(sal_uIntPtr 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);

                // #108867# 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 SetOfByte& rVisLayers,
        Color& rCol,
        bool bSkipBackgroundShape)
    {
	    if(!rPage.GetModel())
		    return false;

	    bool bRet(impGetSdrObjListFillColor(rPage, rPnt, rTextEditPV, rVisLayers, rCol));

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

			    // #108867# 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);
		    }
	    }

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

	    return bRet;
    }

    Color impCalcBackgroundColor(
        const 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_uIntPtr nHeight( rArea.GetSize().Height() );
            sal_uIntPtr nWidth( rArea.GetSize().Width() );
            sal_uIntPtr nWidth14  = nWidth / 4;
            sal_uIntPtr nHeight14 = nHeight / 4;
            sal_uIntPtr nWidth34  = ( 3 * nWidth ) / 4;
            sal_uIntPtr 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].X() += nWidth14;
                        aSpotPos[i].Y() += nHeight14;
                    }
                    break;

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

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

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

                }

                aSpotColor[i] = Color( 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 = dynamic_cast< SdrTextObj * >(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)
			    {
				    Rectangle aSnapRect( pText->GetSnapRect() );
				    aSnapRect.Move(aPvOfs.X(), aPvOfs.Y());

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

    return aBackground;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// eof
