/**************************************************************
 * 
 * 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 <svx/svdedtv.hxx>
#include <editeng/outliner.hxx>
#include <svx/svdundo.hxx>
#include <svx/svdogrp.hxx>   // fuer's Gruppieren
#include <svx/svdovirt.hxx>  // fuer VirtualObject-Bundling (Writer)
#include <svx/svdopath.hxx>  // fuer CombineObjects
#include <svx/svdpage.hxx>
#include <svx/svdpagv.hxx>
#include "svx/svditer.hxx"
#include <svx/svdograf.hxx>  // fuer Possibilities
#include <svx/svdoole2.hxx>  // und Mtf-Import
#include "svx/svdstr.hrc"   // Namen aus der Resource
#include "svx/svdglob.hxx"  // StringCache
#include "svdfmtf.hxx"
#include <svx/svdetc.hxx>
#include <sfx2/basedlgs.hxx>
#include <vcl/msgbox.hxx>
#include <editeng/outlobj.hxx>
#include <editeng/eeitem.hxx>
#include <basegfx/polygon/b2dpolypolygon.hxx>
#include <basegfx/polygon/b2dpolypolygontools.hxx>

#include <svx/svxdlg.hxx> //CHINA001
#include <svx/dialogs.hrc> //CHINA001

// #i37011#
#include <svx/svdoashp.hxx>
#include <basegfx/polygon/b2dpolypolygoncutter.hxx>

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
//
//  @@@@@ @@@@@  @@ @@@@@@  @@ @@ @@ @@@@@ @@   @@
//  @@    @@  @@ @@   @@    @@ @@ @@ @@    @@   @@
//  @@    @@  @@ @@   @@    @@ @@ @@ @@    @@ @ @@
//  @@@@  @@  @@ @@   @@    @@@@@ @@ @@@@  @@@@@@@
//  @@    @@  @@ @@   @@     @@@  @@ @@    @@@@@@@
//  @@    @@  @@ @@   @@     @@@  @@ @@    @@@ @@@
//  @@@@@ @@@@@  @@   @@      @   @@ @@@@@ @@   @@
//
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void SdrEditView::ImpBundleVirtObjOfMarkList()
{
  // ... fehlende Implementation
}

SdrObject* SdrEditView::GetMaxToTopObj(SdrObject* /*pObj*/) const
{
  return NULL;
}

SdrObject* SdrEditView::GetMaxToBtmObj(SdrObject* /*pObj*/) const
{
  return NULL;
}

void SdrEditView::ObjOrderChanged(SdrObject* /*pObj*/, sal_uIntPtr /*nOldPos*/, sal_uIntPtr /*nNewPos*/)
{
}

void SdrEditView::MovMarkedToTop()
{
	sal_uIntPtr nAnz=GetMarkedObjectCount();
	if (nAnz!=0)
	{
		const bool bUndo = IsUndoEnabled();

		if( bUndo )
			BegUndo(ImpGetResStr(STR_EditMovToTop),GetDescriptionOfMarkedObjects(),SDRREPFUNC_OBJ_MOVTOTOP);

		SortMarkedObjects();
		sal_uIntPtr nm;
		for (nm=0; nm<nAnz; nm++)
		{ // Ordnums muessen alle stimmen!
			GetMarkedObjectByIndex(nm)->GetOrdNum();
		}
		sal_Bool bChg=sal_False;
		SdrObjList* pOL0=NULL;
		sal_uIntPtr nNewPos=0;
		for (nm=nAnz; nm>0;)
		{
			nm--;
			SdrMark* pM=GetSdrMarkByIndex(nm);
			SdrObject* pObj=pM->GetMarkedSdrObj();
			SdrObjList* pOL=pObj->GetObjList();
			if (pOL!=pOL0)
			{
				nNewPos=sal_uIntPtr(pOL->GetObjCount()-1);
				pOL0=pOL;
			}
			sal_uIntPtr nNowPos=pObj->GetOrdNumDirect();
			const Rectangle& rBR=pObj->GetCurrentBoundRect();
			sal_uIntPtr nCmpPos=nNowPos+1;
            SdrObject* pMaxObj=GetMaxToTopObj(pObj);
            if (pMaxObj!=NULL)
			{
                sal_uIntPtr nMaxPos=pMaxObj->GetOrdNum();
                if (nMaxPos!=0)
					nMaxPos--;
                if (nNewPos>nMaxPos)
					nNewPos=nMaxPos; // diesen nicht ueberholen.
                if (nNewPos<nNowPos)
					nNewPos=nNowPos; // aber dabei auch nicht in die falsche Richtung schieben
            }
			sal_Bool bEnd=sal_False;
			while (nCmpPos<nNewPos && !bEnd)
			{
				SdrObject* pCmpObj=pOL->GetObj(nCmpPos);
				if (pCmpObj==NULL)
				{
					DBG_ERROR("MovMarkedToTop(): Vergleichsobjekt nicht gefunden");
					bEnd=sal_True;
                }
				else if (pCmpObj==pMaxObj)
				{
                    nNewPos=nCmpPos;
                    nNewPos--;
                    bEnd=sal_True;
				}
				else if (rBR.IsOver(pCmpObj->GetCurrentBoundRect()))
				{
					nNewPos=nCmpPos;
					bEnd=sal_True;
				}
				else
				{
					nCmpPos++;
				}
			}
			if (nNowPos!=nNewPos)
			{
				bChg=sal_True;
				pOL->SetObjectOrdNum(nNowPos,nNewPos);
				if( bUndo )
					AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoObjectOrdNum(*pObj,nNowPos,nNewPos));
				ObjOrderChanged(pObj,nNowPos,nNewPos);
			}
			nNewPos--;
		}

		if( bUndo )
			EndUndo();

		if (bChg)
			MarkListHasChanged();
	}
}

void SdrEditView::MovMarkedToBtm()
{
	sal_uIntPtr nAnz=GetMarkedObjectCount();
	if (nAnz!=0) 
	{
		const bool bUndo = IsUndoEnabled();

		if( bUndo )
			BegUndo(ImpGetResStr(STR_EditMovToBtm),GetDescriptionOfMarkedObjects(),SDRREPFUNC_OBJ_MOVTOBTM);

		SortMarkedObjects();
		sal_uIntPtr nm;
		for (nm=0; nm<nAnz; nm++)
		{ // Ordnums muessen alle stimmen!
			GetMarkedObjectByIndex(nm)->GetOrdNum();
		}

		sal_Bool bChg=sal_False;
		SdrObjList* pOL0=NULL;
		sal_uIntPtr nNewPos=0;
		for (nm=0; nm<nAnz; nm++)
		{
			SdrMark* pM=GetSdrMarkByIndex(nm);
			SdrObject* pObj=pM->GetMarkedSdrObj();
			SdrObjList* pOL=pObj->GetObjList();
			if (pOL!=pOL0)
			{
				nNewPos=0;
				pOL0=pOL;
			}
			sal_uIntPtr nNowPos=pObj->GetOrdNumDirect();
			const Rectangle& rBR=pObj->GetCurrentBoundRect();
			sal_uIntPtr nCmpPos=nNowPos; if (nCmpPos>0) nCmpPos--;
            SdrObject* pMaxObj=GetMaxToBtmObj(pObj);
            if (pMaxObj!=NULL)
			{
                sal_uIntPtr nMinPos=pMaxObj->GetOrdNum()+1;
                if (nNewPos<nMinPos)
					nNewPos=nMinPos; // diesen nicht ueberholen.
                if (nNewPos>nNowPos)
					nNewPos=nNowPos; // aber dabei auch nicht in die falsche Richtung schieben
            }
			sal_Bool bEnd=sal_False;
			// nNewPos ist an dieser Stelle noch die maximale Position,
			// an der das Obj hinruecken darf, ohne seinen Vorgaenger
			// (Mehrfachselektion) zu ueberholen.
			while (nCmpPos>nNewPos && !bEnd)
			{
				SdrObject* pCmpObj=pOL->GetObj(nCmpPos);
				if (pCmpObj==NULL)
				{
					DBG_ERROR("MovMarkedToBtm(): Vergleichsobjekt nicht gefunden");
					bEnd=sal_True;
                }
				else if (pCmpObj==pMaxObj)
				{
                    nNewPos=nCmpPos;
                    nNewPos++;
                    bEnd=sal_True;
				}
				else if (rBR.IsOver(pCmpObj->GetCurrentBoundRect()))
				{
					nNewPos=nCmpPos;
					bEnd=sal_True;
				}
				else
				{
					nCmpPos--;
				}
			}
			if (nNowPos!=nNewPos)
			{
				bChg=sal_True;
				pOL->SetObjectOrdNum(nNowPos,nNewPos);
				if( bUndo )
					AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoObjectOrdNum(*pObj,nNowPos,nNewPos));
				ObjOrderChanged(pObj,nNowPos,nNewPos);
			}
			nNewPos++;
		}

		if(bUndo)
			EndUndo();

		if(bChg)
			MarkListHasChanged();
	}
}

void SdrEditView::PutMarkedToTop()
{
	PutMarkedInFrontOfObj(NULL);
}

void SdrEditView::PutMarkedInFrontOfObj(const SdrObject* pRefObj)
{
	sal_uIntPtr nAnz=GetMarkedObjectCount();
	if (nAnz!=0)
	{
		const bool bUndo = IsUndoEnabled();
		if( bUndo )
			BegUndo(ImpGetResStr(STR_EditPutToTop),GetDescriptionOfMarkedObjects(),SDRREPFUNC_OBJ_PUTTOTOP);

		SortMarkedObjects();

		if (pRefObj!=NULL)
		{
			// Damit "Vor das Objekt" auch funktioniert wenn die
			// markierten Objekte bereits vor dem Objekt stehen
			sal_uIntPtr nRefMark=TryToFindMarkedObject(pRefObj);
			SdrMark aRefMark;
			if (nRefMark!=CONTAINER_ENTRY_NOTFOUND)
			{
				aRefMark=*GetSdrMarkByIndex(nRefMark);
				GetMarkedObjectListWriteAccess().DeleteMark(nRefMark);
			}
			PutMarkedToBtm();
			if (nRefMark!=CONTAINER_ENTRY_NOTFOUND)
			{
				GetMarkedObjectListWriteAccess().InsertEntry(aRefMark);
				SortMarkedObjects();
			}
		}
		sal_uIntPtr nm;
		for (nm=0; nm<nAnz; nm++)
		{ // Ordnums muessen alle stimmen!
			GetMarkedObjectByIndex(nm)->GetOrdNum();
		}
		sal_Bool bChg=sal_False;
		SdrObjList* pOL0=NULL;
		sal_uIntPtr nNewPos=0;
		for (nm=nAnz; nm>0;)
		{
			nm--;
			SdrMark* pM=GetSdrMarkByIndex(nm);
			SdrObject* pObj=pM->GetMarkedSdrObj();
			if (pObj!=pRefObj)
			{
				SdrObjList* pOL=pObj->GetObjList();
				if (pOL!=pOL0)
				{
					nNewPos=sal_uIntPtr(pOL->GetObjCount()-1);
					pOL0=pOL;
				}
				sal_uIntPtr nNowPos=pObj->GetOrdNumDirect();
                SdrObject* pMaxObj=GetMaxToTopObj(pObj);
                if (pMaxObj!=NULL)
				{
                    sal_uIntPtr nMaxOrd=pMaxObj->GetOrdNum(); // geht leider nicht anders
                    if (nMaxOrd>0)
						nMaxOrd--;
                    if (nNewPos>nMaxOrd)
						nNewPos=nMaxOrd; // nicht ueberholen.
                    if (nNewPos<nNowPos)
						nNewPos=nNowPos; // aber dabei auch nicht in die falsche Richtung schieben
                }
				if (pRefObj!=NULL)
				{
					if (pRefObj->GetObjList()==pObj->GetObjList())
					{
						sal_uIntPtr nMaxOrd=pRefObj->GetOrdNum(); // geht leider nicht anders
						if (nNewPos>nMaxOrd)
							nNewPos=nMaxOrd; // nicht ueberholen.
						if (nNewPos<nNowPos)
							nNewPos=nNowPos; // aber dabei auch nicht in die falsche Richtung schieben
					}
					else
					{
						nNewPos=nNowPos; // andere PageView, also nicht veraendern
					}
				}
				if (nNowPos!=nNewPos)
				{
					bChg=sal_True;
					pOL->SetObjectOrdNum(nNowPos,nNewPos);
					if( bUndo )
						AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoObjectOrdNum(*pObj,nNowPos,nNewPos));
					ObjOrderChanged(pObj,nNowPos,nNewPos);
				}
				nNewPos--;
			} // if (pObj!=pRefObj)
		} // for-Schleife ueber alle Markierten Objekte

		if( bUndo )
			EndUndo();

		if(bChg)
			MarkListHasChanged();
	}
}

void SdrEditView::PutMarkedToBtm()
{
	PutMarkedBehindObj(NULL);
}

void SdrEditView::PutMarkedBehindObj(const SdrObject* pRefObj)
{
	sal_uIntPtr nAnz=GetMarkedObjectCount();
	if (nAnz!=0)
	{
		const bool bUndo = IsUndoEnabled();

		if( bUndo )
			BegUndo(ImpGetResStr(STR_EditPutToBtm),GetDescriptionOfMarkedObjects(),SDRREPFUNC_OBJ_PUTTOBTM);

		SortMarkedObjects();
		if (pRefObj!=NULL)
		{
			// Damit "Hinter das Objekt" auch funktioniert wenn die
			// markierten Objekte bereits hinter dem Objekt stehen
			sal_uIntPtr nRefMark=TryToFindMarkedObject(pRefObj);
			SdrMark aRefMark;
			if (nRefMark!=CONTAINER_ENTRY_NOTFOUND)
			{
				aRefMark=*GetSdrMarkByIndex(nRefMark);
				GetMarkedObjectListWriteAccess().DeleteMark(nRefMark);
			}
			PutMarkedToTop();
			if (nRefMark!=CONTAINER_ENTRY_NOTFOUND)
			{
				GetMarkedObjectListWriteAccess().InsertEntry(aRefMark);
				SortMarkedObjects();
			}
		}
		sal_uIntPtr nm;
		for (nm=0; nm<nAnz; nm++) { // Ordnums muessen alle stimmen!
			GetMarkedObjectByIndex(nm)->GetOrdNum();
		}
		sal_Bool bChg=sal_False;
		SdrObjList* pOL0=NULL;
		sal_uIntPtr nNewPos=0;
		for (nm=0; nm<nAnz; nm++) {
			SdrMark* pM=GetSdrMarkByIndex(nm);
			SdrObject* pObj=pM->GetMarkedSdrObj();
			if (pObj!=pRefObj) {
				SdrObjList* pOL=pObj->GetObjList();
				if (pOL!=pOL0) {
					nNewPos=0;
					pOL0=pOL;
				}
				sal_uIntPtr nNowPos=pObj->GetOrdNumDirect();
                SdrObject* pMinObj=GetMaxToBtmObj(pObj);
                if (pMinObj!=NULL) {
                    sal_uIntPtr nMinOrd=pMinObj->GetOrdNum()+1; // geht leider nicht anders
                    if (nNewPos<nMinOrd) nNewPos=nMinOrd; // nicht ueberholen.
                    if (nNewPos>nNowPos) nNewPos=nNowPos; // aber dabei auch nicht in die falsche Richtung schieben
                }
				if (pRefObj!=NULL) {
					if (pRefObj->GetObjList()==pObj->GetObjList()) {
						sal_uIntPtr nMinOrd=pRefObj->GetOrdNum(); // geht leider nicht anders
						if (nNewPos<nMinOrd) nNewPos=nMinOrd; // nicht ueberholen.
						if (nNewPos>nNowPos) nNewPos=nNowPos; // aber dabei auch nicht in die falsche Richtung schieben
					} else {
						nNewPos=nNowPos; // andere PageView, also nicht veraendern
					}
				}
				if (nNowPos!=nNewPos) {
					bChg=sal_True;
					pOL->SetObjectOrdNum(nNowPos,nNewPos);
					if( bUndo )
						AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoObjectOrdNum(*pObj,nNowPos,nNewPos));
					ObjOrderChanged(pObj,nNowPos,nNewPos);
				}
				nNewPos++;
			} // if (pObj!=pRefObj)
		} // for-Schleife ueber alle markierten Objekte

		if(bUndo)
			EndUndo();

		if(bChg)
			MarkListHasChanged();
	}
}

void SdrEditView::ReverseOrderOfMarked()
{
	SortMarkedObjects();
	sal_uIntPtr nMarkAnz=GetMarkedObjectCount();
	if (nMarkAnz>0)
	{
		//sal_Bool bNeedBundle=sal_False;
		sal_Bool bChg=sal_False;

		bool bUndo = IsUndoEnabled();
		if( bUndo )
			BegUndo(ImpGetResStr(STR_EditRevOrder),GetDescriptionOfMarkedObjects(),SDRREPFUNC_OBJ_REVORDER);

		sal_uIntPtr a=0;
		do {
			// Markierung ueber mehrere PageViews berueksichtigen
			sal_uIntPtr b=a+1;
			while (b<nMarkAnz && GetSdrPageViewOfMarkedByIndex(b) == GetSdrPageViewOfMarkedByIndex(a)) b++;
			b--;
			SdrObjList* pOL=GetSdrPageViewOfMarkedByIndex(a)->GetObjList();
			sal_uIntPtr c=b;
			if (a<c) { // Sicherstellen, dass die OrdNums nicht Dirty sind
				GetMarkedObjectByIndex(a)->GetOrdNum();
			}
			while (a<c) {
				SdrObject* pObj1=GetMarkedObjectByIndex(a);
				SdrObject* pObj2=GetMarkedObjectByIndex(c);
				sal_uIntPtr nOrd1=pObj1->GetOrdNumDirect();
				sal_uIntPtr nOrd2=pObj2->GetOrdNumDirect();
				if( bUndo )
				{
					AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoObjectOrdNum(*pObj1,nOrd1,nOrd2));
					AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoObjectOrdNum(*pObj2,nOrd2-1,nOrd1));
				}
				pOL->SetObjectOrdNum(nOrd1,nOrd2);
				// Obj 2 ist um eine Position nach vorn gerutscht, deshalb nun nOrd2-1
				pOL->SetObjectOrdNum(nOrd2-1,nOrd1);
				// Verwendung von Replace statt SetOrdNum wg. Performance (Neuberechnung der Ordnums)
				a++; c--;
				bChg=sal_True;
			}
			a=b+1;
		} while (a<nMarkAnz);

		if(bUndo)
			EndUndo();

		if(bChg)
			MarkListHasChanged();
	}
}

void SdrEditView::ImpCheckToTopBtmPossible()
{
	sal_uIntPtr nAnz=GetMarkedObjectCount();
	if (nAnz==0)
		return;
	if (nAnz==1)
	{ // Sonderbehandlung fuer Einzelmarkierung
		SdrObject* pObj=GetMarkedObjectByIndex(0);
		SdrObjList* pOL=pObj->GetObjList();
		sal_uIntPtr nMax=pOL->GetObjCount();
		sal_uIntPtr nMin=0;
		sal_uIntPtr nObjNum=pObj->GetOrdNum();
		SdrObject* pRestrict=GetMaxToTopObj(pObj);
		if (pRestrict!=NULL) {
			sal_uIntPtr nRestrict=pRestrict->GetOrdNum();
			if (nRestrict<nMax) nMax=nRestrict;
		}
		pRestrict=GetMaxToBtmObj(pObj);
		if (pRestrict!=NULL) {
			sal_uIntPtr nRestrict=pRestrict->GetOrdNum();
			if (nRestrict>nMin) nMin=nRestrict;
		}
		bToTopPossible=nObjNum<sal_uIntPtr(nMax-1);
		bToBtmPossible=nObjNum>nMin;
	} else { // Mehrfachselektion
		sal_uIntPtr nm=0;
		SdrObjList* pOL0=NULL;
		long nPos0=-1;
		while (!bToBtmPossible && nm<nAnz) { // 'nach hinten' checken
			SdrObject* pObj=GetMarkedObjectByIndex(nm);
			SdrObjList* pOL=pObj->GetObjList();
			if (pOL!=pOL0) {
				nPos0=-1;
				pOL0=pOL;
			}
			sal_uIntPtr nPos=pObj->GetOrdNum();
			bToBtmPossible=nPos>sal_uIntPtr(nPos0+1);
			nPos0=long(nPos);
			nm++;
		}
		nm=nAnz;
		pOL0=NULL;
		nPos0=0x7FFFFFFF;
		while (!bToTopPossible && nm>0) { // 'nach vorn' checken
			nm--;
			SdrObject* pObj=GetMarkedObjectByIndex(nm);
			SdrObjList* pOL=pObj->GetObjList();
			if (pOL!=pOL0) {
				nPos0=pOL->GetObjCount();
				pOL0=pOL;
			}
			sal_uIntPtr nPos=pObj->GetOrdNum();
			bToTopPossible=nPos+1<sal_uIntPtr(nPos0);
			nPos0=nPos;
		}
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////
//
//   @@@@    @@@@   @@   @@  @@@@@   @@  @@  @@  @@@@@
//  @@  @@  @@  @@  @@@ @@@  @@  @@  @@  @@@ @@  @@
//  @@      @@  @@  @@@@@@@  @@  @@  @@  @@@@@@  @@
//  @@      @@  @@  @@@@@@@  @@@@@   @@  @@@@@@  @@@@
//  @@      @@  @@  @@ @ @@  @@  @@  @@  @@ @@@  @@
//  @@  @@  @@  @@  @@   @@  @@  @@  @@  @@  @@  @@
//   @@@@    @@@@   @@   @@  @@@@@   @@  @@  @@  @@@@@
//
////////////////////////////////////////////////////////////////////////////////////////////////////

void SdrEditView::ImpCopyAttributes(const SdrObject* pSource, SdrObject* pDest) const
{
	if (pSource!=NULL) {
		SdrObjList* pOL=pSource->GetSubList();
		if (pOL!=NULL && !pSource->Is3DObj()) { // erstes Nichtgruppenobjekt aus der Gruppe holen
			SdrObjListIter aIter(*pOL,IM_DEEPNOGROUPS);
			pSource=aIter.Next();
		}
	}

	if(pSource && pDest)
	{
		SfxItemSet aSet(pMod->GetItemPool(),
			SDRATTR_START,				SDRATTR_NOTPERSIST_FIRST-1,
			SDRATTR_NOTPERSIST_LAST+1,	SDRATTR_END,
			EE_ITEMS_START,				EE_ITEMS_END,
			0, 0); // #52757#, #52762#

		aSet.Put(pSource->GetMergedItemSet());

		pDest->ClearMergedItem();
		pDest->SetMergedItemSet(aSet);

		pDest->NbcSetLayer(pSource->GetLayer());
		pDest->NbcSetStyleSheet(pSource->GetStyleSheet(), sal_True);
	}
}

sal_Bool SdrEditView::ImpCanConvertForCombine1(const SdrObject* pObj) const
{
	// #69711 : new condition IsLine() to be able to combine simple Lines
	sal_Bool bIsLine(sal_False);

	const SdrPathObj* pPath = PTR_CAST(SdrPathObj,pObj);

	if(pPath)
	{
		bIsLine = pPath->IsLine();
	}

	SdrObjTransformInfoRec aInfo;
	pObj->TakeObjInfo(aInfo);

	return (aInfo.bCanConvToPath || aInfo.bCanConvToPoly || bIsLine);
}

sal_Bool SdrEditView::ImpCanConvertForCombine(const SdrObject* pObj) const
{
	SdrObjList* pOL = pObj->GetSubList();

	if(pOL && !pObj->Is3DObj()) 
	{
		SdrObjListIter aIter(*pOL, IM_DEEPNOGROUPS);

		while(aIter.IsMore()) 
		{
			SdrObject* pObj1 = aIter.Next();

			// Es muessen alle Member einer Gruppe konvertierbar sein
			if(!ImpCanConvertForCombine1(pObj1)) 
			{
				return sal_False;
			}
		}
	} 
	else 
	{
		if(!ImpCanConvertForCombine1(pObj)) 
		{
			return sal_False;
		}
	}

	return sal_True;
}

basegfx::B2DPolyPolygon SdrEditView::ImpGetPolyPolygon1(const SdrObject* pObj, sal_Bool bCombine) const
{
	basegfx::B2DPolyPolygon aRetval;
	SdrPathObj* pPath = PTR_CAST(SdrPathObj, pObj);

	if(bCombine && pPath && !pObj->GetOutlinerParaObject()) 
	{
		aRetval = pPath->GetPathPoly();
	} 
	else 
	{
		SdrObject* pConvObj = pObj->ConvertToPolyObj(bCombine, sal_False);

		if(pConvObj) 
		{
			SdrObjList* pOL = pConvObj->GetSubList();
			
			if(pOL) 
			{
				SdrObjListIter aIter(*pOL, IM_DEEPNOGROUPS);

				while(aIter.IsMore()) 
				{
					SdrObject* pObj1 = aIter.Next();
					pPath = PTR_CAST(SdrPathObj, pObj1);

					if(pPath) 
					{
						aRetval.append(pPath->GetPathPoly());
					}
				}
			} 
			else 
			{
				pPath = PTR_CAST(SdrPathObj, pConvObj);

				if(pPath) 
				{
					aRetval = pPath->GetPathPoly();
				}
			}

			SdrObject::Free( pConvObj );
		}
	}

	return aRetval;
}

basegfx::B2DPolyPolygon SdrEditView::ImpGetPolyPolygon(const SdrObject* pObj, sal_Bool bCombine) const
{
	SdrObjList* pOL = pObj->GetSubList();

	if(pOL && !pObj->Is3DObj()) 
	{
		basegfx::B2DPolyPolygon aRetval;
		SdrObjListIter aIter(*pOL, IM_DEEPNOGROUPS);

		while(aIter.IsMore()) 
		{
			SdrObject* pObj1 = aIter.Next();
			aRetval.append(ImpGetPolyPolygon1(pObj1, bCombine));
		}

		return aRetval;
	} 
	else 
	{
		return ImpGetPolyPolygon1(pObj, bCombine);
	}
}

basegfx::B2DPolygon SdrEditView::ImpCombineToSinglePolygon(const basegfx::B2DPolyPolygon& rPolyPolygon) const
{
	const sal_uInt32 nPolyCount(rPolyPolygon.count());

	if(0L == nPolyCount)
	{
		return basegfx::B2DPolygon();
	}
	else if(1L == nPolyCount)
	{
		return rPolyPolygon.getB2DPolygon(0L);
	}
	else
	{
		basegfx::B2DPolygon aRetval(rPolyPolygon.getB2DPolygon(0L));

		for(sal_uInt32 a(1L); a < nPolyCount; a++)
		{
			basegfx::B2DPolygon aCandidate(rPolyPolygon.getB2DPolygon(a));

			if(aRetval.count())
			{
				if(aCandidate.count())
				{
					const basegfx::B2DPoint aCA(aCandidate.getB2DPoint(0L));
					const basegfx::B2DPoint aCB(aCandidate.getB2DPoint(aCandidate.count() - 1L));
					const basegfx::B2DPoint aRA(aRetval.getB2DPoint(0L));
					const basegfx::B2DPoint aRB(aRetval.getB2DPoint(aRetval.count() - 1L));
					
					const double fRACA(basegfx::B2DVector(aCA - aRA).getLength());
					const double fRACB(basegfx::B2DVector(aCB - aRA).getLength());
					const double fRBCA(basegfx::B2DVector(aCA - aRB).getLength());
					const double fRBCB(basegfx::B2DVector(aCB - aRB).getLength());
					
					const double fSmallestRA(fRACA < fRACB ? fRACA : fRACB);
					const double fSmallestRB(fRBCA < fRBCB ? fRBCA : fRBCB);

					if(fSmallestRA < fSmallestRB)
					{
						// flip result
						aRetval.flip();
					}

					const double fSmallestCA(fRACA < fRBCA ? fRACA : fRBCA);
					const double fSmallestCB(fRACB < fRBCB ? fRACB : fRBCB);

					if(fSmallestCB < fSmallestCA)
					{
						// flip candidate
						aCandidate.flip();
					}

					// append candidate to retval
					aRetval.append(aCandidate);
				}
			}
			else
			{
				aRetval = aCandidate;
			}
		}

		return aRetval;
	}
}

// for distribution dialog function
struct ImpDistributeEntry
{
	SdrObject*					mpObj;
	sal_Int32						mnPos;
	sal_Int32						mnLength;
};

DECLARE_LIST(ImpDistributeEntryList, ImpDistributeEntry*)

void SdrEditView::DistributeMarkedObjects()
{
	sal_uInt32 nMark(GetMarkedObjectCount());

	if(nMark > 2)
	{
		SfxItemSet aNewAttr(pMod->GetItemPool());
		//CHINA001 SvxDistributeDialog* pDlg = new SvxDistributeDialog(NULL, aNewAttr);
		SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
		if(pFact)
		{
			AbstractSvxDistributeDialog *pDlg = pFact->CreateSvxDistributeDialog(NULL, aNewAttr);
			DBG_ASSERT(pDlg, "Dialogdiet fail!");//CHINA001

			sal_uInt16 nResult = pDlg->Execute();

			if(nResult == RET_OK)
			{
				SvxDistributeHorizontal eHor = pDlg->GetDistributeHor();
				SvxDistributeVertical eVer = pDlg->GetDistributeVer();
				ImpDistributeEntryList aEntryList;
				sal_uInt32 a, nInsPos, nFullLength;

				const bool bUndo = IsUndoEnabled();
				if( bUndo )
					BegUndo();

				if(eHor != SvxDistributeHorizontalNone)
				{
					// build sorted entry list
					nFullLength = 0L;

					for(a=0;a<nMark;a++)
					{
						SdrMark* pMark = GetSdrMarkByIndex(a);
						ImpDistributeEntry* pNew = new ImpDistributeEntry;

						pNew->mpObj = pMark->GetMarkedSdrObj();
						nInsPos = 0;

						switch(eHor)
						{
							case SvxDistributeHorizontalLeft:
							{
								pNew->mnPos = pNew->mpObj->GetSnapRect().Left();
								break;
							}
							case SvxDistributeHorizontalCenter:
							{
								pNew->mnPos = (pNew->mpObj->GetSnapRect().Right() + pNew->mpObj->GetSnapRect().Left()) / 2;
								break;
							}
							case SvxDistributeHorizontalDistance:
							{
								pNew->mnLength = pNew->mpObj->GetSnapRect().GetWidth() + 1;
								nFullLength += pNew->mnLength;
								pNew->mnPos = (pNew->mpObj->GetSnapRect().Right() + pNew->mpObj->GetSnapRect().Left()) / 2;
								break;
							}
							case SvxDistributeHorizontalRight:
							{
								pNew->mnPos = pNew->mpObj->GetSnapRect().Right();
								break;
							}
							default: break;
						}

						while(nInsPos < aEntryList.Count() && aEntryList.GetObject(nInsPos)->mnPos < pNew->mnPos)
							nInsPos++;

						aEntryList.Insert(pNew, nInsPos);
					}

					if(eHor == SvxDistributeHorizontalDistance)
					{
						// calc room in-between
						sal_Int32 nWidth = GetAllMarkedBoundRect().GetWidth() + 1;
						double fStepWidth = ((double)nWidth - (double)nFullLength) / (double)(aEntryList.Count() - 1);
						double fStepStart = (double)aEntryList.GetObject(0)->mnPos;
						fStepStart += fStepWidth + (double)((aEntryList.GetObject(0)->mnLength + aEntryList.GetObject(1)->mnLength) / 2);

						// move entries 1..n-1
						for(a=1;a<aEntryList.Count()-1;a++)
						{
							ImpDistributeEntry* pCurr = aEntryList.GetObject(a);
							ImpDistributeEntry* pNext = aEntryList.GetObject(a+1);
							sal_Int32 nDelta = (sal_Int32)(fStepStart + 0.5) - pCurr->mnPos;
							if( bUndo )
								AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pCurr->mpObj));
							pCurr->mpObj->Move(Size(nDelta, 0));
							fStepStart += fStepWidth + (double)((pCurr->mnLength + pNext->mnLength) / 2);
						}
					}
					else
					{
						// calc distances
						sal_Int32 nWidth = aEntryList.GetObject(aEntryList.Count() - 1)->mnPos - aEntryList.GetObject(0)->mnPos;
						double fStepWidth = (double)nWidth / (double)(aEntryList.Count() - 1);
						double fStepStart = (double)aEntryList.GetObject(0)->mnPos;
						fStepStart += fStepWidth;

						// move entries 1..n-1
						for(a=1;a<aEntryList.Count()-1;a++)
						{
							ImpDistributeEntry* pCurr = aEntryList.GetObject(a);
							sal_Int32 nDelta = (sal_Int32)(fStepStart + 0.5) - pCurr->mnPos;
							if( bUndo )
								AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pCurr->mpObj));
							pCurr->mpObj->Move(Size(nDelta, 0));
							fStepStart += fStepWidth;
						}
					}

					// clear list
					while(aEntryList.Count())
						delete aEntryList.Remove((sal_uIntPtr)0L);
				}

				if(eVer != SvxDistributeVerticalNone)
				{
					// build sorted entry list
					nFullLength = 0L;

					for(a=0;a<nMark;a++)
					{
						SdrMark* pMark = GetSdrMarkByIndex(a);
						ImpDistributeEntry* pNew = new ImpDistributeEntry;

						pNew->mpObj = pMark->GetMarkedSdrObj();
						nInsPos = 0;

						switch(eVer)
						{
							case SvxDistributeVerticalTop:
							{
								pNew->mnPos = pNew->mpObj->GetSnapRect().Top();
								break;
							}
							case SvxDistributeVerticalCenter:
							{
								pNew->mnPos = (pNew->mpObj->GetSnapRect().Bottom() + pNew->mpObj->GetSnapRect().Top()) / 2;
								break;
							}
							case SvxDistributeVerticalDistance:
							{
								pNew->mnLength = pNew->mpObj->GetSnapRect().GetHeight() + 1;
								nFullLength += pNew->mnLength;
								pNew->mnPos = (pNew->mpObj->GetSnapRect().Bottom() + pNew->mpObj->GetSnapRect().Top()) / 2;
								break;
							}
							case SvxDistributeVerticalBottom:
							{
								pNew->mnPos = pNew->mpObj->GetSnapRect().Bottom();
								break;
							}
							default: break;
						}

						while(nInsPos < aEntryList.Count() && aEntryList.GetObject(nInsPos)->mnPos < pNew->mnPos)
							nInsPos++;

						aEntryList.Insert(pNew, nInsPos);
					}

					if(eVer == SvxDistributeVerticalDistance)
					{
						// calc room in-between
						sal_Int32 nHeight = GetAllMarkedBoundRect().GetHeight() + 1;
						double fStepWidth = ((double)nHeight - (double)nFullLength) / (double)(aEntryList.Count() - 1);
						double fStepStart = (double)aEntryList.GetObject(0)->mnPos;
						fStepStart += fStepWidth + (double)((aEntryList.GetObject(0)->mnLength + aEntryList.GetObject(1)->mnLength) / 2);

						// move entries 1..n-1
						for(a=1;a<aEntryList.Count()-1;a++)
						{
							ImpDistributeEntry* pCurr = aEntryList.GetObject(a);
							ImpDistributeEntry* pNext = aEntryList.GetObject(a+1);
							sal_Int32 nDelta = (sal_Int32)(fStepStart + 0.5) - pCurr->mnPos;
							if( bUndo )
								AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pCurr->mpObj));
							pCurr->mpObj->Move(Size(0, nDelta));
							fStepStart += fStepWidth + (double)((pCurr->mnLength + pNext->mnLength) / 2);
						}
					}
					else
					{
						// calc distances
						sal_Int32 nHeight = aEntryList.GetObject(aEntryList.Count() - 1)->mnPos - aEntryList.GetObject(0)->mnPos;
						double fStepWidth = (double)nHeight / (double)(aEntryList.Count() - 1);
						double fStepStart = (double)aEntryList.GetObject(0)->mnPos;
						fStepStart += fStepWidth;

						// move entries 1..n-1
						for(a=1;a<aEntryList.Count()-1;a++)
						{
							ImpDistributeEntry* pCurr = aEntryList.GetObject(a);
							sal_Int32 nDelta = (sal_Int32)(fStepStart + 0.5) - pCurr->mnPos;
							if( bUndo )
								AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pCurr->mpObj));
							pCurr->mpObj->Move(Size(0, nDelta));
							fStepStart += fStepWidth;
						}
					}

					// clear list
					while(aEntryList.Count())
						delete aEntryList.Remove((sal_uIntPtr)0L);
				}

				// UNDO-Comment and end of UNDO
				SetUndoComment(ImpGetResStr(STR_DistributeMarkedObjects));

				if( bUndo )
					EndUndo();
			}

			delete(pDlg);
		}
	}
}

void SdrEditView::MergeMarkedObjects(SdrMergeMode eMode)
{
	// #i73441# check content
	if(AreObjectsMarked())
	{
		SdrMarkList aRemove;
		SortMarkedObjects();

		const bool bUndo = IsUndoEnabled();

		if( bUndo )
			BegUndo();

		sal_uInt32 nInsPos=0xFFFFFFFF;
		const SdrObject* pAttrObj = NULL;
		basegfx::B2DPolyPolygon aMergePolyPolygonA;
		basegfx::B2DPolyPolygon aMergePolyPolygonB;

		SdrObjList* pInsOL = NULL;
		SdrPageView* pInsPV = NULL;
		sal_Bool bFirstObjectComplete(sal_False);

		// make sure selected objects are contour objects
		// since now basegfx::tools::adaptiveSubdivide() is used, it is no longer
		// necessary to use ConvertMarkedToPolyObj which will subdivide curves using the old
		// mechanisms. In a next step the polygon clipper will even be able to clip curves...
		// ConvertMarkedToPolyObj(sal_True);
		ConvertMarkedToPathObj(sal_True);
		OSL_ENSURE(AreObjectsMarked(), "no more objects selected after preparations (!)");

		for(sal_uInt32 a=0;a<GetMarkedObjectCount();a++)
		{
			SdrMark* pM = GetSdrMarkByIndex(a);
			SdrObject* pObj = pM->GetMarkedSdrObj();

			if(ImpCanConvertForCombine(pObj))
			{
				if(!pAttrObj)
					pAttrObj = pObj;

				nInsPos = pObj->GetOrdNum() + 1;
				pInsPV = pM->GetPageView();
				pInsOL = pObj->GetObjList();

				// #i76891# use single iter from SJ here whcih works on SdrObjects and takes
				// groups into account by itself
				SdrObjListIter aIter(*pObj, IM_DEEPWITHGROUPS);

				while(aIter.IsMore())
				{
					SdrObject* pCandidate = aIter.Next();
					SdrPathObj* pPathObj = PTR_CAST(SdrPathObj, pCandidate);
					if(pPathObj)
					{
						basegfx::B2DPolyPolygon aTmpPoly(pPathObj->GetPathPoly());

						// #i76891# unfortunately ConvertMarkedToPathObj has converted all
						// involved polygon data to curve segments, even if not necessary.
						// It is better to try to reduce to more simple polygons.
						aTmpPoly = basegfx::tools::simplifyCurveSegments(aTmpPoly);

						// for each part polygon as preparation, remove self-intersections
						// correct orientations and get rid of evtl. neutral polygons.
	                    aTmpPoly = basegfx::tools::prepareForPolygonOperation(aTmpPoly);

						if(!bFirstObjectComplete)
						{
                            // #i111987# Also need to collect ORed source shape when more than
                            // a single polygon is involved
                            if(aMergePolyPolygonA.count())
                            {
                                aMergePolyPolygonA = basegfx::tools::solvePolygonOperationOr(aMergePolyPolygonA, aTmpPoly);
                            }
                            else
                            {
    							aMergePolyPolygonA = aTmpPoly;
                            }
						}
						else
						{
                            if(aMergePolyPolygonB.count())
                            {
                                // to topologically correctly collect the 2nd polygon
                                // group it is necessary to OR the parts (each is seen as
                                // XOR-FillRule polygon and they are drawn over each-other)
                                aMergePolyPolygonB = basegfx::tools::solvePolygonOperationOr(aMergePolyPolygonB, aTmpPoly);
                            }
                            else
                            {
    							aMergePolyPolygonB = aTmpPoly;
                            }
						}
					}
				}

				// was there something added to the first poly?
				if(!bFirstObjectComplete && aMergePolyPolygonA.count())
				{
					bFirstObjectComplete = sal_True;
				}

				// move object to temporary delete list
				aRemove.InsertEntry(SdrMark(pObj, pM->GetPageView()));
			}
		}

		switch(eMode)
		{
			case SDR_MERGE_MERGE:
			{
				// merge all contained parts (OR)
                static bool bTestXOR(false);
                if(bTestXOR)
                {
                    aMergePolyPolygonA = basegfx::tools::solvePolygonOperationXor(aMergePolyPolygonA, aMergePolyPolygonB);
                }
                else
                {
                    aMergePolyPolygonA = basegfx::tools::solvePolygonOperationOr(aMergePolyPolygonA, aMergePolyPolygonB);
                }
				break;
			}
			case SDR_MERGE_SUBSTRACT:
			{
				// Substract B from A
                aMergePolyPolygonA = basegfx::tools::solvePolygonOperationDiff(aMergePolyPolygonA, aMergePolyPolygonB);
				break;
			}
			case SDR_MERGE_INTERSECT:
			{
				// AND B and A
                aMergePolyPolygonA = basegfx::tools::solvePolygonOperationAnd(aMergePolyPolygonA, aMergePolyPolygonB);
				break;
			}
		}

		// #i73441# check insert list before taking actions
		if(pInsOL)
		{
			SdrPathObj* pPath = new SdrPathObj(OBJ_PATHFILL, aMergePolyPolygonA);
			ImpCopyAttributes(pAttrObj, pPath);
			SdrInsertReason aReason(SDRREASON_VIEWCALL, pAttrObj);
			pInsOL->InsertObject(pPath, nInsPos, &aReason);
			if( bUndo )
				AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pPath));
			MarkObj(pPath, pInsPV, sal_False, sal_True);
		}

		aRemove.ForceSort();
		switch(eMode)
		{
			case SDR_MERGE_MERGE:
			{
				SetUndoComment(
					ImpGetResStr(STR_EditMergeMergePoly),
					aRemove.GetMarkDescription());
				break;
			}
			case SDR_MERGE_SUBSTRACT:
			{
				SetUndoComment(
					ImpGetResStr(STR_EditMergeSubstractPoly),
					aRemove.GetMarkDescription());
				break;
			}
			case SDR_MERGE_INTERSECT:
			{
				SetUndoComment(
					ImpGetResStr(STR_EditMergeIntersectPoly),
					aRemove.GetMarkDescription());
				break;
			}
		}
		DeleteMarkedList(aRemove);

		if( bUndo )
			EndUndo();
	}
}

void SdrEditView::CombineMarkedObjects(sal_Bool bNoPolyPoly)
{
	// #105899# Start of Combine-Undo put to front, else ConvertMarkedToPolyObj would
	// create a 2nd Undo-action and Undo-Comment.

	bool bUndo = IsUndoEnabled();

	// Undo-String will be set later
	if( bUndo )
		BegUndo(String(), String(), bNoPolyPoly ? SDRREPFUNC_OBJ_COMBINE_ONEPOLY : SDRREPFUNC_OBJ_COMBINE_POLYPOLY);

	// #105899# First, guarantee that all objects are converted to polyobjects,
	// especially for SdrGrafObj with bitmap filling this is necessary to not
	// loose the bitmap filling.

	// #i12392#
	// ConvertMarkedToPolyObj was too strong here, it will loose quality and
	// information when curve objects are combined. This can be replaced by
	// using ConvertMarkedToPathObj without changing the previous fix.

	// #i21250#
	// Instead of simply passing sal_True as LineToArea, use bNoPolyPoly as info
	// if this command is a 'Combine' or a 'Connect' command. On Connect it's sal_True.
	// To not concert line segments with a set line width to polygons in that case,
	// use this info. Do not convert LineToArea on Connect commands.
	// ConvertMarkedToPathObj(!bNoPolyPoly);

	// #114310#
	// This is used for Combine and Connect. In no case it is necessary to force
	// the content to curve, but it is also not good to force to polygons. Thus,
	// curve is the less information loosing one. Remember: This place is not
	// used for merge.
	// LineToArea is never necessary, both commands are able to take over the
	// set line style and to display it correctly. Thus, i will use a
	// ConvertMarkedToPathObj with a sal_False in any case. Only drawback is that
	// simple polygons will be changed to curves, but with no information loss.
	ConvertMarkedToPathObj(sal_False /* bLineToArea */);

	// continue as before
	basegfx::B2DPolyPolygon aPolyPolygon;
	SdrObjList* pAktOL = 0L;
	SdrMarkList aRemoveMerker;

	SortMarkedObjects();
	sal_uInt32 nInsPos(0xFFFFFFFF);
	SdrObjList* pInsOL = 0L;
	SdrPageView* pInsPV = 0L;
	const sal_uInt32 nAnz(GetMarkedObjectCount());
	const SdrObject* pAttrObj = 0L;

	for(sal_uInt32 a(nAnz); a > 0L; ) 
	{
		a--;
		SdrMark* pM = GetSdrMarkByIndex(a);
		SdrObject* pObj = pM->GetMarkedSdrObj();
		SdrObjList* pThisOL = pObj->GetObjList();

		if(pAktOL != pThisOL) 
		{
			pAktOL = pThisOL;
		}

		if(ImpCanConvertForCombine(pObj)) 
		{
			// Obj merken fuer Attribute kopieren
			pAttrObj = pObj;

			// unfortunately ConvertMarkedToPathObj has converted all
			// involved polygon data to curve segments, even if not necessary.
			// It is better to try to reduce to more simple polygons.
			basegfx::B2DPolyPolygon aTmpPoly(basegfx::tools::simplifyCurveSegments(ImpGetPolyPolygon(pObj, sal_True)));
			aPolyPolygon.insert(0L, aTmpPoly);

			if(!pInsOL) 
			{
				nInsPos = pObj->GetOrdNum() + 1L;
				pInsPV = pM->GetPageView();
				pInsOL = pObj->GetObjList();
			}

			aRemoveMerker.InsertEntry(SdrMark(pObj, pM->GetPageView()));
		}
	}

	if(bNoPolyPoly) 
	{
		basegfx::B2DPolygon aCombinedPolygon(ImpCombineToSinglePolygon(aPolyPolygon));
		aPolyPolygon.clear();
		aPolyPolygon.append(aCombinedPolygon);
	}

	const sal_uInt32 nPolyCount(aPolyPolygon.count());

	if(nPolyCount) 
	{
		SdrObjKind eKind = OBJ_PATHFILL;

		if(nPolyCount > 1L) 
		{
			aPolyPolygon.setClosed(true);
		} 
		else 
		{ 
			// auf Polyline Checken
			const basegfx::B2DPolygon aPolygon(aPolyPolygon.getB2DPolygon(0L));
			const sal_uInt32 nPointCount(aPolygon.count());
			
			if(nPointCount <= 2L) 
			{
				eKind = OBJ_PATHLINE;
			}
			else 
			{
				if(!aPolygon.isClosed())
				{
					const basegfx::B2DPoint aPointA(aPolygon.getB2DPoint(0L));
					const basegfx::B2DPoint aPointB(aPolygon.getB2DPoint(nPointCount - 1L));
					const double fDistance(basegfx::B2DVector(aPointB - aPointA).getLength());
					const double fJoinTolerance(10.0);

					if(fDistance < fJoinTolerance)
					{
						aPolyPolygon.setClosed(true);
					}
					else
					{
						eKind = OBJ_PATHLINE;
					}
				}
			}
		}

		SdrPathObj* pPath = new SdrPathObj(eKind,aPolyPolygon);

		// Attribute des untersten Objekts
		ImpCopyAttributes(pAttrObj, pPath);

		// #100408# If LineStyle of pAttrObj is XLINE_NONE force to XLINE_SOLID to make visible.
		const XLineStyle eLineStyle = ((const XLineStyleItem&)pAttrObj->GetMergedItem(XATTR_LINESTYLE)).GetValue();
		const XFillStyle eFillStyle = ((const XFillStyleItem&)pAttrObj->GetMergedItem(XATTR_FILLSTYLE)).GetValue();

		// #110635#
		// Take fill style/closed state of pAttrObj in account when deciding to change the line style
		sal_Bool bIsClosedPathObj(pAttrObj->ISA(SdrPathObj) && ((SdrPathObj*)pAttrObj)->IsClosed());

		if(XLINE_NONE == eLineStyle && (XFILL_NONE == eFillStyle || !bIsClosedPathObj))
		{
			pPath->SetMergedItem(XLineStyleItem(XLINE_SOLID));
		}

		SdrInsertReason aReason(SDRREASON_VIEWCALL,pAttrObj);
		pInsOL->InsertObject(pPath,nInsPos,&aReason);
		if( bUndo )
			AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pPath));

		// #111111#
		// Here was a severe error: Without UnmarkAllObj, the new object was marked
		// additionally to the two ones which are deleted below. As long as those are
		// in the UNDO there is no problem, but as soon as they get deleted, the
		// MarkList will contain deleted objects -> GPF.
		UnmarkAllObj(pInsPV);
		MarkObj(pPath, pInsPV, sal_False, sal_True);
	}

	// UndoComment aus den tatsaechlich verwendeten Objekten zusammenbauen
	aRemoveMerker.ForceSort(); // wichtig fuer Remove (s.u.)
	if( bUndo )
		SetUndoComment(ImpGetResStr(bNoPolyPoly?STR_EditCombine_OnePoly:STR_EditCombine_PolyPoly),aRemoveMerker.GetMarkDescription());

	// die tatsaechlich verwendeten Objekten aus der Liste entfernen
	DeleteMarkedList(aRemoveMerker);
	if( bUndo )
		EndUndo();
}

////////////////////////////////////////////////////////////////////////////////////////////////////
//
//  @@@@@   @@   @@@@   @@   @@   @@@@   @@  @@  @@@@@@  @@     @@@@@
//  @@  @@  @@  @@  @@  @@@ @@@  @@  @@  @@@ @@    @@    @@     @@
//  @@  @@  @@  @@      @@@@@@@  @@  @@  @@@@@@    @@    @@     @@
//  @@  @@  @@   @@@@   @@@@@@@  @@@@@@  @@@@@@    @@    @@     @@@@
//  @@  @@  @@      @@  @@ @ @@  @@  @@  @@ @@@    @@    @@     @@
//  @@  @@  @@  @@  @@  @@   @@  @@  @@  @@  @@    @@    @@     @@
//  @@@@@   @@   @@@@   @@   @@  @@  @@  @@  @@    @@    @@@@@  @@@@@
//
////////////////////////////////////////////////////////////////////////////////////////////////////

sal_Bool SdrEditView::ImpCanDismantle(const basegfx::B2DPolyPolygon& rPpolyPolygon, sal_Bool bMakeLines) const
{
	sal_Bool bCan(sal_False);
	const sal_uInt32 nPolygonCount(rPpolyPolygon.count());

	if(nPolygonCount >= 2L) 
	{
		// #i69172# dismantle makes sense with 2 or more polygons in a polyPolygon
		bCan = sal_True;
	}
	else if(bMakeLines && 1L == nPolygonCount) 
	{ 
		// #i69172# ..or with at least 2 edges (curves or lines)
		const basegfx::B2DPolygon aPolygon(rPpolyPolygon.getB2DPolygon(0L));
		const sal_uInt32 nPointCount(aPolygon.count());
		
		if(nPointCount > 2L)
		{
			bCan = sal_True;
		}
	}

	return bCan;
}

sal_Bool SdrEditView::ImpCanDismantle(const SdrObject* pObj, sal_Bool bMakeLines) const
{
	sal_Bool bOtherObjs(sal_False);    // sal_True=andere Objekte ausser PathObj's vorhanden
	sal_Bool bMin1PolyPoly(sal_False); // sal_True=mind. 1 PolyPolygon mit mehr als ein Polygon vorhanden
	SdrObjList* pOL = pObj->GetSubList();

	if(pOL) 
	{
		// Aha, Gruppenobjekt. Also alle Member ansehen.
		// Alle muessen PathObjs sein !
		SdrObjListIter aIter(*pOL, IM_DEEPNOGROUPS);

		while(aIter.IsMore() && !bOtherObjs) 
		{
			const SdrObject* pObj1 = aIter.Next();
			const SdrPathObj* pPath = PTR_CAST(SdrPathObj, pObj1);

			if(pPath) 
			{
				if(ImpCanDismantle(pPath->GetPathPoly(), bMakeLines)) 
				{
					bMin1PolyPoly = sal_True;
				}

				SdrObjTransformInfoRec aInfo;
				pObj1->TakeObjInfo(aInfo);

				if(!aInfo.bCanConvToPath) 
				{
					// Passiert z.B. im Falle Fontwork (Joe, 28-11-95)
					bOtherObjs = sal_True; 
				}
			} 
			else 
			{
				bOtherObjs = sal_True;
			}
		}
	}
	else
	{
		const SdrPathObj* pPath = PTR_CAST(SdrPathObj, pObj);
		const SdrObjCustomShape* pCustomShape = PTR_CAST(SdrObjCustomShape, pObj);

		// #i37011#
		if(pPath)
		{
			if(ImpCanDismantle(pPath->GetPathPoly(),bMakeLines)) 
			{
				bMin1PolyPoly = sal_True;
			}

			SdrObjTransformInfoRec aInfo;
			pObj->TakeObjInfo(aInfo);

			// #69711 : new condition IsLine() to be able to break simple Lines
			if(!(aInfo.bCanConvToPath || aInfo.bCanConvToPoly) && !pPath->IsLine()) 
			{
				// Passiert z.B. im Falle Fontwork (Joe, 28-11-95)
				bOtherObjs = sal_True; 
			}
		} 
		else if(pCustomShape)
		{
			if(bMakeLines)
			{
				// allow break command
				bMin1PolyPoly = sal_True;
			}
		}
		else
		{
			bOtherObjs = sal_True;
		}
	}
	return bMin1PolyPoly && !bOtherObjs;
}

void SdrEditView::ImpDismantleOneObject(const SdrObject* pObj, SdrObjList& rOL, sal_uIntPtr& rPos, SdrPageView* pPV, sal_Bool bMakeLines)
{
	const SdrPathObj* pSrcPath = PTR_CAST(SdrPathObj, pObj);
	const SdrObjCustomShape* pCustomShape = PTR_CAST(SdrObjCustomShape, pObj);

	const bool bUndo = IsUndoEnabled();

	if(pSrcPath)
	{
		// #i74631# redesigned due to XpolyPolygon removal and explicit constructors
		SdrObject* pLast = 0; // fuer die Zuweisung des OutlinerParaObject
		const basegfx::B2DPolyPolygon& rPolyPolygon(pSrcPath->GetPathPoly());
		const sal_uInt32 nPolyCount(rPolyPolygon.count());

		for(sal_uInt32 a(0); a < nPolyCount; a++) 
		{
			const basegfx::B2DPolygon& rCandidate(rPolyPolygon.getB2DPolygon(a));
			const sal_uInt32 nPointCount(rCandidate.count());

			if(!bMakeLines || nPointCount < 2)
			{
				SdrPathObj* pPath = new SdrPathObj((SdrObjKind)pSrcPath->GetObjIdentifier(), basegfx::B2DPolyPolygon(rCandidate));
				ImpCopyAttributes(pSrcPath, pPath);
				pLast = pPath;
				SdrInsertReason aReason(SDRREASON_VIEWCALL, pSrcPath);
				rOL.InsertObject(pPath, rPos, &aReason);
				if( bUndo )
					AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pPath, sal_True));
				MarkObj(pPath, pPV, sal_False, sal_True);
				rPos++;
			}
			else
			{
				const sal_uInt32 nLoopCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);

				for(sal_uInt32 b(0); b < nLoopCount; b++)
				{
					SdrObjKind eKind(OBJ_PLIN);
					basegfx::B2DPolygon aNewPolygon;
					const sal_uInt32 nNextIndex((b + 1) % nPointCount);

					aNewPolygon.append(rCandidate.getB2DPoint(b));

					if(rCandidate.areControlPointsUsed())
					{
						aNewPolygon.appendBezierSegment(
							rCandidate.getNextControlPoint(b),
							rCandidate.getPrevControlPoint(nNextIndex),
							rCandidate.getB2DPoint(nNextIndex));
						eKind = OBJ_PATHLINE;
					}
					else
					{
						aNewPolygon.append(rCandidate.getB2DPoint(nNextIndex));
					}

					SdrPathObj* pPath = new SdrPathObj(eKind, basegfx::B2DPolyPolygon(aNewPolygon));
					ImpCopyAttributes(pSrcPath, pPath);
					pLast = pPath;
					SdrInsertReason aReason(SDRREASON_VIEWCALL, pSrcPath);
					rOL.InsertObject(pPath, rPos, &aReason);
					if( bUndo )
						AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pPath, sal_True));
					MarkObj(pPath, pPV, sal_False, sal_True);
					rPos++;
				}
			}
		}

		if(pLast && pSrcPath->GetOutlinerParaObject())
		{
			pLast->SetOutlinerParaObject(new OutlinerParaObject(*pSrcPath->GetOutlinerParaObject()));
		}
	}
	else if(pCustomShape)
	{
		if(bMakeLines)
		{
			// break up custom shape
			const SdrObject* pReplacement = pCustomShape->GetSdrObjectFromCustomShape();

			if(pReplacement)
			{
				SdrObject* pCandidate = pReplacement->Clone();
				DBG_ASSERT(pCandidate, "SdrEditView::ImpDismantleOneObject: Could not clone SdrObject (!)");
				pCandidate->SetModel(pCustomShape->GetModel());

				if(((SdrShadowItem&)pCustomShape->GetMergedItem(SDRATTR_SHADOW)).GetValue())
				{
					if(pReplacement->ISA(SdrObjGroup))
					{
						pCandidate->SetMergedItem(SdrShadowItem(sal_True));
					}
				}

				SdrInsertReason aReason(SDRREASON_VIEWCALL, pCustomShape);
				rOL.InsertObject(pCandidate, rPos, &aReason);
				if( bUndo )
					AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pCandidate, true));
				MarkObj(pCandidate, pPV, sal_False, sal_True);

				if(pCustomShape->HasText() && !pCustomShape->IsTextPath())
				{
					// #i37011# also create a text object and add at rPos + 1
					SdrTextObj* pTextObj = (SdrTextObj*)SdrObjFactory::MakeNewObject(
						pCustomShape->GetObjInventor(), OBJ_TEXT, 0L, pCustomShape->GetModel());

					// Copy text content
					OutlinerParaObject* pParaObj = pCustomShape->GetOutlinerParaObject();
					if(pParaObj)
					{
						pTextObj->NbcSetOutlinerParaObject(new OutlinerParaObject(*pParaObj));
					}

					// copy all attributes
					SfxItemSet aTargetItemSet(pCustomShape->GetMergedItemSet());

					// clear fill and line style
					aTargetItemSet.Put(XLineStyleItem(XLINE_NONE));
					aTargetItemSet.Put(XFillStyleItem(XFILL_NONE));

					// get the text bounds and set at text object
					Rectangle aTextBounds = pCustomShape->GetSnapRect();
					if(pCustomShape->GetTextBounds(aTextBounds))
					{
						pTextObj->SetSnapRect(aTextBounds);
					}

					// if rotated, copy GeoStat, too.
					const GeoStat& rSourceGeo = pCustomShape->GetGeoStat();
					if(rSourceGeo.nDrehWink)
					{
						pTextObj->NbcRotate(
							pCustomShape->GetSnapRect().Center(), rSourceGeo.nDrehWink,
							rSourceGeo.nSin, rSourceGeo.nCos);
					}

					// set modified ItemSet at text object
					pTextObj->SetMergedItemSet(aTargetItemSet);

					// insert object
					rOL.InsertObject(pTextObj, rPos + 1, &aReason);
					if( bUndo )
						AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pTextObj, true));
					MarkObj(pTextObj, pPV, sal_False, sal_True);
				}
			}
		}
	}
}

void SdrEditView::DismantleMarkedObjects(sal_Bool bMakeLines)
{
	//sal_uInt32 nCnt(0);
	// Temporaere Marklist
	SdrMarkList aRemoveMerker;

	SortMarkedObjects();

	const bool bUndo = IsUndoEnabled();

	if( bUndo )
	{
		// Der Comment wird spaeter zusammengebaut
		BegUndo(String(), String(),
			bMakeLines ? SDRREPFUNC_OBJ_DISMANTLE_LINES : SDRREPFUNC_OBJ_DISMANTLE_POLYS);
	}

	sal_uIntPtr nm;
	sal_uIntPtr nAnz=GetMarkedObjectCount();
	SdrObjList* pOL0=NULL;
	for (nm=nAnz; nm>0;) {
		nm--;
		SdrMark* pM=GetSdrMarkByIndex(nm);
		SdrObject* pObj=pM->GetMarkedSdrObj();
		SdrPageView* pPV=pM->GetPageView();
		SdrObjList* pOL=pObj->GetObjList();
		if (pOL!=pOL0) { pOL0=pOL; pObj->GetOrdNum(); } // sicherstellen, dass OrdNums stimmen!
		if (ImpCanDismantle(pObj,bMakeLines)) {
			aRemoveMerker.InsertEntry(SdrMark(pObj,pM->GetPageView()));
			sal_uIntPtr nPos0=pObj->GetOrdNumDirect();
			sal_uIntPtr nPos=nPos0+1;
			SdrObjList* pSubList=pObj->GetSubList();
			if (pSubList!=NULL && !pObj->Is3DObj()) {
				SdrObjListIter aIter(*pSubList,IM_DEEPNOGROUPS);
				while (aIter.IsMore()) {
					const SdrObject* pObj1=aIter.Next();
					ImpDismantleOneObject(pObj1,*pOL,nPos,pPV,bMakeLines);
				}
			} else {
				ImpDismantleOneObject(pObj,*pOL,nPos,pPV,bMakeLines);
			}
			if( bUndo )
				AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoDeleteObject(*pObj,sal_True));
			pOL->RemoveObject(nPos0);

			if( !bUndo )
				SdrObject::Free(pObj);
		}
	}

	if( bUndo )
	{
		// UndoComment aus den tatsaechlich verwendeten Objekten zusammenbauen
		SetUndoComment(ImpGetResStr(bMakeLines?STR_EditDismantle_Lines:STR_EditDismantle_Polys),aRemoveMerker.GetMarkDescription());
		// die tatsaechlich verwendeten Objekten aus der Liste entfernen
		EndUndo();
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////
//
//   #### ####   ###  #   # ####
//  #     #   # #   # #   # #   #
//  #  ## ####  #   # #   # ####
//  #   # #   # #   # #   # #
//   #### #   #  ###   ###  #
//
////////////////////////////////////////////////////////////////////////////////////////////////////

void SdrEditView::GroupMarked(const SdrObject* pUserGrp)
{
	if (AreObjectsMarked())
	{
		SortMarkedObjects();

		const bool bUndo = IsUndoEnabled();
		if( bUndo )
		{
			BegUndo(ImpGetResStr(STR_EditGroup),GetDescriptionOfMarkedObjects(),SDRREPFUNC_OBJ_GROUP);

			const sal_uIntPtr nAnz = GetMarkedObjectCount();
			for(sal_uIntPtr nm = nAnz; nm>0; )
			{
				// UndoActions fuer alle betroffenen Objekte anlegen
				nm--;
				SdrMark* pM=GetSdrMarkByIndex(nm);
				SdrObject* pObj = pM->GetMarkedSdrObj();
					std::vector< SdrUndoAction* > vConnectorUndoActions( CreateConnectorUndo( *pObj ) );
					AddUndoActions( vConnectorUndoActions );
					AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoRemoveObject( *pObj ));
			}
		}

		SdrMarkList aNewMark;
		SdrPageView* pPV = GetSdrPageView();

		if(pPV)
		{
			SdrObjList* pAktLst=pPV->GetObjList();
			SdrObjList* pSrcLst=pAktLst;
			SdrObjList* pSrcLst0=pSrcLst;
			SdrPage*    pPage=pPV->GetPage();
			// sicherstellen, dass die OrdNums stimmen
			if (pSrcLst->IsObjOrdNumsDirty())
				pSrcLst->RecalcObjOrdNums();
			SdrObject*  pGrp=NULL;
			SdrObject*  pRefObj=NULL; // Referenz fuer InsertReason (-> rumankern im Writer)
			SdrObject*  pRefObj1=NULL; // Referenz fuer InsertReason (-> rumankern im Writer)
			SdrObjList* pDstLst=NULL;
			// Falls alle markierten Objekte aus Fremden Obj-Listen
			// kommen, kommt das Gruppenobjekt an das Ende der Liste.
			sal_uIntPtr       nInsPos=pSrcLst->GetObjCount();
			sal_Bool    bNeedInsPos=sal_True;
			for (sal_uIntPtr nm=GetMarkedObjectCount(); nm>0;)
			{
				nm--;
				SdrMark* pM=GetSdrMarkByIndex(nm);
				if (pM->GetPageView()==pPV)
				{
					if (pGrp==NULL)
					{
						if (pUserGrp!=NULL)
							pGrp=pUserGrp->Clone();
						if (pGrp==NULL)
							pGrp=new SdrObjGroup;
						pDstLst=pGrp->GetSubList();
						DBG_ASSERT(pDstLst!=NULL,"Angebliches Gruppenobjekt liefert keine Objektliste");
					}
					SdrObject* pObj=pM->GetMarkedSdrObj();
					pSrcLst=pObj->GetObjList();
					if (pSrcLst!=pSrcLst0)
					{
						if (pSrcLst->IsObjOrdNumsDirty())
							pSrcLst->RecalcObjOrdNums();
					}
					sal_Bool bForeignList=pSrcLst!=pAktLst;
					sal_Bool bGrouped=pSrcLst!=pPage;
					if (!bForeignList && bNeedInsPos)
					{
						nInsPos=pObj->GetOrdNum(); // ua, damit sind alle ObjOrdNum der Page gesetzt
						nInsPos++;
						bNeedInsPos=sal_False;
					}
					pSrcLst->RemoveObject(pObj->GetOrdNumDirect());
					if (!bForeignList)
						nInsPos--; // InsertPos korregieren
					SdrInsertReason aReason(SDRREASON_VIEWCALL);
					pDstLst->InsertObject(pObj,0,&aReason);
					GetMarkedObjectListWriteAccess().DeleteMark(nm);
					if (pRefObj1==NULL)
						pRefObj1=pObj; // Das oberste sichtbare Objekt
					if (!bGrouped)
					{
						if (pRefObj==NULL)
							pRefObj=pObj; // Das oberste sichtbare nicht gruppierte Objekt
					}
					pSrcLst0=pSrcLst;
				}
			}
			if (pRefObj==NULL)
				pRefObj=pRefObj1;
			if (pGrp!=NULL)
			{
				aNewMark.InsertEntry(SdrMark(pGrp,pPV));
				sal_uIntPtr nAnz=pDstLst->GetObjCount();
				SdrInsertReason aReason(SDRREASON_VIEWCALL,pRefObj);
				pAktLst->InsertObject(pGrp,nInsPos,&aReason);
				if( bUndo )
				{
					AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pGrp,true)); // Kein Recalc!
					for (sal_uIntPtr no=0; no<nAnz; no++)
					{
						AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoInsertObject(*pDstLst->GetObj(no)));
					}
				}
			}
		}
		GetMarkedObjectListWriteAccess().Merge(aNewMark);
		MarkListHasChanged();

		if( bUndo )
			EndUndo();
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////
//
//  #   # #   #  #### ####   ###  #   # ####
//  #   # ##  # #     #   # #   # #   # #   #
//  #   # # # # #  ## ####  #   # #   # ####
//  #   # #  ## #   # #   # #   # #   # #
//   ###  #   #  #### #   #  ###   ###  #
//
////////////////////////////////////////////////////////////////////////////////////////////////////

void SdrEditView::UnGroupMarked()
{
	SdrMarkList aNewMark;

	const bool bUndo = IsUndoEnabled();
	if( bUndo )
		BegUndo(String(), String(), SDRREPFUNC_OBJ_UNGROUP);

	sal_uIntPtr nCount=0;
	XubString aName1;
	XubString aName;
	sal_Bool bNameOk=sal_False;
	for (sal_uIntPtr nm=GetMarkedObjectCount(); nm>0;) {
		nm--;
		SdrMark* pM=GetSdrMarkByIndex(nm);
		SdrObject* pGrp=pM->GetMarkedSdrObj();
		SdrObjList* pSrcLst=pGrp->GetSubList();
		if (pSrcLst!=NULL) {
			nCount++;
			if (nCount==1) {
				pGrp->TakeObjNameSingul(aName);  // Bezeichnung der Gruppe holen
				pGrp->TakeObjNamePlural(aName1); // Bezeichnung der Gruppe holen
				bNameOk=sal_True;
			} else {
				if (nCount==2) aName=aName1; // Pluralname setzen
				if (bNameOk) {
					XubString aStr;
					pGrp->TakeObjNamePlural(aStr); // Bezeichnung der Gruppe holen

					if(!aStr.Equals(aName))
						bNameOk = sal_False;
				}
			}
			sal_uIntPtr nDstCnt=pGrp->GetOrdNum();
			SdrObjList* pDstLst=pM->GetPageView()->GetObjList();

			// FIRST move contained objects to parent of group, so that
			// the contained objects are NOT migrated to the UNDO-ItemPool
			// when AddUndo(new SdrUndoDelObj(*pGrp)) is called.
			sal_uIntPtr nAnz=pSrcLst->GetObjCount();
			sal_uIntPtr no;

			if( bUndo )
			{
				for (no=nAnz; no>0;)
				{
					no--;
					SdrObject* pObj=pSrcLst->GetObj(no);
					AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoRemoveObject(*pObj));
				}
			}
			for (no=0; no<nAnz; no++)
			{
				SdrObject* pObj=pSrcLst->RemoveObject(0);
				SdrInsertReason aReason(SDRREASON_VIEWCALL,pGrp);
				pDstLst->InsertObject(pObj,nDstCnt,&aReason);
				if( bUndo )
					AddUndo( GetModel()->GetSdrUndoFactory().CreateUndoInsertObject(*pObj,true));
				nDstCnt++;
				// Kein SortCheck beim einfuegen in die MarkList, denn das
				// wuerde wg. pObj->GetOrdNum() jedesmal ein RecalcOrdNums()
				// provozieren:
				aNewMark.InsertEntry(SdrMark(pObj,pM->GetPageView()),sal_False);
			}

			if( bUndo )
			{
				// Now it is safe to add the delete-UNDO which trigers the
				// MigrateItemPool now only for itself, not for the subobjects.
				// nDstCnt is right, because previous inserts move group
				// object deeper and increase nDstCnt.
				AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoDeleteObject(*pGrp));
			}
			pDstLst->RemoveObject(nDstCnt);

			if( !bUndo )
				SdrObject::Free(pGrp);

			GetMarkedObjectListWriteAccess().DeleteMark(nm);
		}
	}
	if (nCount!=0)
	{
		if (!bNameOk)
			aName=ImpGetResStr(STR_ObjNamePluralGRUP); // Oberbegriff Gruppenobjekte verwenden, wenn verschiedene Objekte.
		SetUndoComment(ImpGetResStr(STR_EditUngroup),aName);
	}

	if( bUndo )
		EndUndo();

	if (nCount!=0)
	{
		GetMarkedObjectListWriteAccess().Merge(aNewMark,sal_True); // Durch das obige Einsortieren ist aNewMark genau verkehrtherum
		MarkListHasChanged();
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////
//
//   ###   ###  #   # #   # ##### ####  #####   #####  ###    ####   ###  #  #   #
//  #   # #   # ##  # #   # #     #   #   #       #   #   #   #   # #   # #   # #
//  #     #   # # # # #   # ####  ####    #       #   #   #   ####  #   # #    #
//  #   # #   # #  ##  # #  #     #   #   #       #   #   #   #     #   # #    #
//   ###   ###  #   #   #   ##### #   #   #       #    ###    #      ###  #### #
//
////////////////////////////////////////////////////////////////////////////////////////////////////

SdrObject* SdrEditView::ImpConvertOneObj(SdrObject* pObj, sal_Bool bPath, sal_Bool bLineToArea)
{
	SdrObject* pNewObj = pObj->ConvertToPolyObj(bPath, bLineToArea);
	if (pNewObj!=NULL)
	{
		SdrObjList* pOL=pObj->GetObjList();
		DBG_ASSERT(pOL!=NULL,"ConvertTo: Obj liefert keine ObjList");
		if (pOL!=NULL)
		{
			const bool bUndo = IsUndoEnabled();
			if( bUndo )
				AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoReplaceObject(*pObj,*pNewObj));

			pOL->ReplaceObject(pNewObj,pObj->GetOrdNum());

			if( !bUndo )
				SdrObject::Free(pObj);
		}
	}
	return pNewObj;
}

void SdrEditView::ImpConvertTo(sal_Bool bPath, sal_Bool bLineToArea)
{
	sal_Bool bMrkChg=sal_False;
	sal_Bool bModChg=sal_False;
	if (AreObjectsMarked()) {
		sal_uIntPtr nMarkAnz=GetMarkedObjectCount();
		sal_uInt16 nDscrID=0;
		if(bLineToArea)
		{
			if(nMarkAnz == 1)
				nDscrID = STR_EditConvToContour;
			else
				nDscrID = STR_EditConvToContours;

			BegUndo(ImpGetResStr(nDscrID), GetDescriptionOfMarkedObjects());
		}
		else
		{
			if (bPath) {
				if (nMarkAnz==1) nDscrID=STR_EditConvToCurve;
				else nDscrID=STR_EditConvToCurves;
				BegUndo(ImpGetResStr(nDscrID),GetDescriptionOfMarkedObjects(),SDRREPFUNC_OBJ_CONVERTTOPATH);
			} else {
				if (nMarkAnz==1) nDscrID=STR_EditConvToPoly;
				else nDscrID=STR_EditConvToPolys;
				BegUndo(ImpGetResStr(nDscrID),GetDescriptionOfMarkedObjects(),SDRREPFUNC_OBJ_CONVERTTOPOLY);
			}
		}
		for (sal_uIntPtr nm=nMarkAnz; nm>0;) {
			nm--;
			SdrMark* pM=GetSdrMarkByIndex(nm);
			SdrObject* pObj=pM->GetMarkedSdrObj();
			SdrPageView* pPV=pM->GetPageView();
			if (pObj->IsGroupObject() && !pObj->Is3DObj()) {
				SdrObject* pGrp=pObj;
				SdrObjListIter aIter(*pGrp,IM_DEEPNOGROUPS);
				while (aIter.IsMore()) {
					pObj=aIter.Next();
					if (ImpConvertOneObj(pObj,bPath,bLineToArea)) bModChg=sal_True;
				}
			} else {
				SdrObject* pNewObj=ImpConvertOneObj(pObj,bPath,bLineToArea);
				if (pNewObj!=NULL) {
					bModChg=sal_True;
					bMrkChg=sal_True;
					GetMarkedObjectListWriteAccess().ReplaceMark(SdrMark(pNewObj,pPV),nm);
				}
			}
		}
		EndUndo();
		if (bMrkChg) AdjustMarkHdl();
		if (bMrkChg) MarkListHasChanged();
	}
}

void SdrEditView::ConvertMarkedToPathObj(sal_Bool bLineToArea)
{
	ImpConvertTo(sal_True, bLineToArea);
}

void SdrEditView::ConvertMarkedToPolyObj(sal_Bool bLineToArea)
{
	ImpConvertTo(sal_False, bLineToArea);
}

////////////////////////////////////////////////////////////////////////////////////////////////////
//
//  #   # ##### #####  ###  ##### # #    #####      # #   # ####   ###  ####  #####
//  ## ## #       #   #   # #     # #    #          # ## ## #   # #   # #   #   #
//  # # # ####    #   ##### ###   # #    ####  ###  # # # # ####  #   # ####    #
//  #   # #       #   #   # #     # #    #          # #   # #     #   # #   #   #
//  #   # #####   #   #   # #     # #### #####      # #   # #      ###  #   #   #
//
////////////////////////////////////////////////////////////////////////////////////////////////////

void SdrEditView::DoImportMarkedMtf(SvdProgressInfo *pProgrInfo)
{
	const bool bUndo = IsUndoEnabled();

	if( bUndo )
		BegUndo(String(), String(), SDRREPFUNC_OBJ_IMPORTMTF);

	SortMarkedObjects();
	SdrMarkList aForTheDescription;
	SdrMarkList aNewMarked;
	sal_uIntPtr nAnz=GetMarkedObjectCount();

	for (sal_uIntPtr nm=nAnz; nm>0;)
	{ // Undo Objekte fuer alle neuen Objekte erzeugen
		// zwischen den Metafiles auf Abbruch testen
		if( pProgrInfo != NULL )
		{
			pProgrInfo->SetNextObject();
			if(!pProgrInfo->ReportActions(0))
				break;
		}

		nm--;
		SdrMark*     pM=GetSdrMarkByIndex(nm);
		SdrObject*   pObj=pM->GetMarkedSdrObj();
		SdrPageView* pPV=pM->GetPageView();
		SdrObjList*  pOL=pObj->GetObjList();
		sal_uIntPtr        nInsPos=pObj->GetOrdNum()+1;
		SdrGrafObj*  pGraf=PTR_CAST(SdrGrafObj,pObj);
		SdrOle2Obj*  pOle2=PTR_CAST(SdrOle2Obj,pObj);
		sal_uIntPtr        nInsAnz=0;
        Rectangle aLogicRect;

		if(pGraf && (pGraf->HasGDIMetaFile() || pGraf->isEmbeddedSvg()))
        {
            GDIMetaFile aMetaFile;

            if(pGraf->HasGDIMetaFile())
            {
                aMetaFile = pGraf->GetTransformedGraphic(SDRGRAFOBJ_TRANSFORMATTR_COLOR|SDRGRAFOBJ_TRANSFORMATTR_MIRROR).GetGDIMetaFile();
            }
            else if(pGraf->isEmbeddedSvg())
            {
                aMetaFile = pGraf->getMetafileFromEmbeddedSvg();
            }

            if(aMetaFile.GetActionCount())
            {
                aLogicRect = pGraf->GetLogicRect();
                ImpSdrGDIMetaFileImport aFilter(*pMod, pObj->GetLayer(), aLogicRect);
                nInsAnz = aFilter.DoImport(aMetaFile, *pOL, nInsPos, pProgrInfo);
            }
        }
        if ( pOle2!=NULL && pOle2->GetGraphic() )
		{
            aLogicRect = pOle2->GetLogicRect();
			ImpSdrGDIMetaFileImport aFilter(*pMod, pObj->GetLayer(), aLogicRect);
            nInsAnz = aFilter.DoImport(pOle2->GetGraphic()->GetGDIMetaFile(), *pOL, nInsPos, pProgrInfo);
		}
		if (nInsAnz!=0)
		{
            // transformation
            GeoStat aGeoStat(pGraf ? pGraf->GetGeoStat() : pOle2->GetGeoStat());
            sal_uIntPtr nObj=nInsPos;

	        if(aGeoStat.nShearWink) 
            {
                aGeoStat.RecalcTan();
            }
	        
            if(aGeoStat.nDrehWink) 
            {
	            aGeoStat.RecalcSinCos();
            }

            for (sal_uIntPtr i=0; i<nInsAnz; i++)
			{
                if( bUndo )
					AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pOL->GetObj(nObj)));

				// Neue MarkList pflegen
                SdrObject* pCandidate = pOL->GetObj(nObj);

                // apply original transformation
	            if(aGeoStat.nShearWink) 
                {
                    pCandidate->NbcShear(aLogicRect.TopLeft(), aGeoStat.nShearWink, aGeoStat.nTan, false);
                }

	            if(aGeoStat.nDrehWink) 
                {
                    pCandidate->NbcRotate(aLogicRect.TopLeft(), aGeoStat.nDrehWink, aGeoStat.nSin, aGeoStat.nCos);
                }

                SdrMark aNewMark(pCandidate, pPV);
				aNewMarked.InsertEntry(aNewMark);

				nObj++;
			}
			aForTheDescription.InsertEntry(*pM);

			if( bUndo )
				AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoDeleteObject(*pObj));

			// Objekt aus selektion loesen und loeschen
			GetMarkedObjectListWriteAccess().DeleteMark(TryToFindMarkedObject(pObj));
			pOL->RemoveObject(nInsPos-1);

			if( !bUndo )
				SdrObject::Free(pObj);
		}
	}

	// MarkObj... fehlt... jetzt nicht mehr (AW)
	if(aNewMarked.GetMarkCount())
	{
		// Neue Selektion bilden
		for(sal_uIntPtr a(0); a < aNewMarked.GetMarkCount(); a++)
		{
			GetMarkedObjectListWriteAccess().InsertEntry(*aNewMarked.GetMark(a));
		}

		SortMarkedObjects();
	}

	if( bUndo )
	{
		SetUndoComment(ImpGetResStr(STR_EditImportMtf),aForTheDescription.GetMarkDescription());
		EndUndo();
	}
}

