/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/


#include <svx/svdedtv.hxx>
#include <editeng/outliner.hxx>
#include <svx/svdundo.hxx>
#include <svx/svdogrp.hxx>   // for grouping objects
#include <svx/svdovirt.hxx>  // for VirtualObject bundling (Writer)
#include <svx/svdopath.hxx>  // for CombineObjects
#include <svx/svdpage.hxx>
#include <svx/svdpagv.hxx>
#include "svx/svditer.hxx"
#include <svx/svdograf.hxx>  // for Possibilities
#include <svx/svdoole2.hxx>  // and Mtf-Import
#include "svx/svdstr.hrc"   // names taken from the 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>
#include <svx/dialogs.hrc>

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

#include <vector>
using ::std::vector;

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++)
        { // All Ordnums have to be correct!
            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; // neither go faster...
                if (nNewPos<nNowPos)
                    nNewPos=nNowPos; // nor go in the other direction
            }
            sal_Bool bEnd=sal_False;
            while (nCmpPos<nNewPos && !bEnd)
            {
                SdrObject* pCmpObj=pOL->GetObj(nCmpPos);
                if (pCmpObj==NULL)
                {
                    OSL_FAIL("MovMarkedToTop(): Reference object not found.");
                    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++)
        { // All Ordnums have to be correct!
            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; // neither go faster...
                if (nNewPos>nNowPos)
                    nNewPos=nNowPos; // nor go in the other direction
            }
            sal_Bool bEnd=sal_False;
            // nNewPos in this case is the "maximum" position
            // the object may reach without going faster than the object before
            // it (multiple selection).
            while (nCmpPos>nNewPos && !bEnd)
            {
                SdrObject* pCmpObj=pOL->GetObj(nCmpPos);
                if (pCmpObj==NULL)
                {
                    OSL_FAIL("MovMarkedToBtm(): Reference object not found.");
                    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)
        {
            // Make "in front of the object" work, even if the
            // selected objects are already in front of the other object
            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++)
        { // All Ordnums have to be correct!
            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(); // sadly doesn't work any other way
                    if (nMaxOrd>0)
                        nMaxOrd--;
                    if (nNewPos>nMaxOrd)
                        nNewPos=nMaxOrd; // neither go faster...
                    if (nNewPos<nNowPos)
                        nNewPos=nNowPos; // nor go into the other direction
                }
                if (pRefObj!=NULL)
                {
                    if (pRefObj->GetObjList()==pObj->GetObjList())
                    {
                        sal_uIntPtr nMaxOrd=pRefObj->GetOrdNum(); // sadly doesn't work any other way
                        if (nNewPos>nMaxOrd)
                            nNewPos=nMaxOrd; // neither go faster...
                        if (nNewPos<nNowPos)
                            nNewPos=nNowPos; // nor go into the other direction
                    }
                    else
                    {
                        nNewPos=nNowPos; // different PageView, so don't change
                    }
                }
                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 loop over all selected objects

        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)
        {
            // Make "behind the object" work, even if the
            // selected objects are already behind the other object
            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++) { // All Ordnums have to be correct!
            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; // sadly doesn't work any differently
                    if (nNewPos<nMinOrd) nNewPos=nMinOrd; // neither go faster...
                    if (nNewPos>nNowPos) nNewPos=nNowPos; // nor go into the other direction
                }
                if (pRefObj!=NULL) {
                    if (pRefObj->GetObjList()==pObj->GetObjList()) {
                        sal_uIntPtr nMinOrd=pRefObj->GetOrdNum(); // sadly doesn't work any differently
                        if (nNewPos<nMinOrd) nNewPos=nMinOrd; // neither go faster...
                        if (nNewPos>nNowPos) nNewPos=nNowPos; // nor go into the other direction
                    } else {
                        nNewPos=nNowPos; // different PageView, so don't change
                    }
                }
                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 loop over all selected objects

        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 {
            // take into account selection across multiple PageViews
            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) { // make sure OrdNums aren't dirty
                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 has moved forward by one position, so now nOrd2-1
                pOL->SetObjectOrdNum(nOrd2-1,nOrd1);
                // use Replace instead of SetOrdNum for performance reasons (recalculation of 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)
    { // special-casing for single selection
        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 { // multiple selection
        sal_uIntPtr nm=0;
        SdrObjList* pOL0=NULL;
        long nPos0=-1;
        while (!bToBtmPossible && nm<nAnz) { // check 'send to background'
            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) { // check 'bring to front'
            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;
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Combine
////////////////////////////////////////////////////////////////////////////////////////////////////

void SdrEditView::ImpCopyAttributes(const SdrObject* pSource, SdrObject* pDest) const
{
    if (pSource!=NULL) {
        SdrObjList* pOL=pSource->GetSubList();
        if (pOL!=NULL && !pSource->Is3DObj()) { // get first non-group object from group
            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);

        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
{
    // 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();

            // all members of a group have to be convertible
            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;
};

typedef vector< ImpDistributeEntry*> ImpDistributeEntryList;

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

    if(nMark > 2)
    {
        SfxItemSet aNewAttr(pMod->GetItemPool());

        SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
        if(pFact)
        {
            AbstractSvxDistributeDialog *pDlg = pFact->CreateSvxDistributeDialog(NULL, aNewAttr);
            DBG_ASSERT(pDlg, "Dialogdiet fail!");

            sal_uInt16 nResult = pDlg->Execute();

            if(nResult == RET_OK)
            {
                SvxDistributeHorizontal eHor = pDlg->GetDistributeHor();
                SvxDistributeVertical eVer = pDlg->GetDistributeVer();
                ImpDistributeEntryList aEntryList;
                ImpDistributeEntryList::iterator itEntryList;
                sal_uInt32 nFullLength;

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

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

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

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

                        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;
                        }

                        for ( itEntryList = aEntryList.begin();
                              itEntryList < aEntryList.end() && (*itEntryList)->mnPos < pNew->mnPos;
                              ++itEntryList )
                        {};
                        if ( itEntryList < aEntryList.end() )
                            aEntryList.insert( itEntryList, pNew );
                        else
                            aEntryList.push_back( pNew );
                    }

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

                        // move entries 1..n-1
                        for( size_t i = 1, n = aEntryList.size()-1; i < n; ++i )
                        {
                            ImpDistributeEntry* pCurr = aEntryList[ i    ];
                            ImpDistributeEntry* pNext = aEntryList[ i + 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
                    {
                        // calculate distances
                        sal_Int32 nWidth = aEntryList[ aEntryList.size() - 1 ]->mnPos - aEntryList[ 0 ]->mnPos;
                        double fStepWidth = (double)nWidth / (double)(aEntryList.size() - 1);
                        double fStepStart = (double)aEntryList[ 0 ]->mnPos;
                        fStepStart += fStepWidth;

                        // move entries 1..n-1
                        for( size_t i = 1 ; i < aEntryList.size()-1 ; ++i )
                        {
                            ImpDistributeEntry* pCurr = aEntryList[ i ];
                            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
                    for ( size_t i = 0, n = aEntryList.size(); i < n; ++i )
                        delete aEntryList[ i ];
                    aEntryList.clear();
                }

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

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

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

                        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;
                        }

                        for ( itEntryList = aEntryList.begin();
                              itEntryList < aEntryList.end() && (*itEntryList)->mnPos < pNew->mnPos;
                              ++itEntryList )
                        {};
                        if ( itEntryList < aEntryList.end() )
                            aEntryList.insert( itEntryList, pNew );
                        else
                            aEntryList.push_back( pNew );
                    }

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

                        // move entries 1..n-1
                        for( size_t i = 1, n = aEntryList.size()-1; i < n; ++i)
                        {
                            ImpDistributeEntry* pCurr = aEntryList[ i     ];
                            ImpDistributeEntry* pNext = aEntryList[ i + 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
                    {
                        // calculate distances
                        sal_Int32 nHeight = aEntryList[ aEntryList.size() - 1 ]->mnPos - aEntryList[ 0 ]->mnPos;
                        double fStepWidth = (double)nHeight / (double)(aEntryList.size() - 1);
                        double fStepStart = (double)aEntryList[ 0 ]->mnPos;
                        fStepStart += fStepWidth;

                        // move entries 1..n-1
                        for(size_t i = 1, n = aEntryList.size()-1; i < n; ++i)
                        {
                            ImpDistributeEntry* pCurr = aEntryList[ i ];
                            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
                    for ( size_t i = 0, n = aEntryList.size(); i < n; ++i )
                        delete aEntryList[ i ];
                    aEntryList.clear();
                }

                // 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 iteration from SJ here which 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 possible 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 polygon?
                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);

    // 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))
        {
            // remember objects to be able to copy attributes
            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
        {
            // check for Polyline
            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);

        // attributes of the lowest object
        ImpCopyAttributes(pAttrObj, pPath);

        // 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();

        // 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));

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

    // build an UndoComment from the objects actually used
    aRemoveMerker.ForceSort(); // important for remove (see below)
    if( bUndo )
        SetUndoComment(ImpGetResStr(bNoPolyPoly?STR_EditCombine_OnePoly:STR_EditCombine_PolyPoly),aRemoveMerker.GetMarkDescription());

    // remove objects actually used from the list
    DeleteMarkedList(aRemoveMerker);
    if( bUndo )
        EndUndo();
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Dismantle
////////////////////////////////////////////////////////////////////////////////////////////////////

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=objects other than PathObj's existent
    sal_Bool bMin1PolyPoly(sal_False); // sal_True=at least 1 PolyPolygon with more than one Polygon existent
    SdrObjList* pOL = pObj->GetSubList();

    if(pOL)
    {
        // group object -- check all members if they're PathObjs
        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)
                {
                    // happens e. g. in the case of FontWork
                    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);

            // new condition IsLine() to be able to break simple Lines
            if(!(aInfo.bCanConvToPath || aInfo.bCanConvToPoly) && !pPath->IsLine())
            {
                // happens e. g. in the case of FontWork
                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; // to be able to apply 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);
    // temporary MarkList
    SdrMarkList aRemoveMerker;

    SortMarkedObjects();

    const bool bUndo = IsUndoEnabled();

    if( bUndo )
    {
        // comment is constructed later
        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(); } // make sure OrdNums are correct!
        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 )
    {
        // construct UndoComment from objects actually used
        SetUndoComment(ImpGetResStr(bMakeLines?STR_EditDismantle_Lines:STR_EditDismantle_Polys),aRemoveMerker.GetMarkDescription());
        // remove objects actually used from the list
        EndUndo();
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Group
////////////////////////////////////////////////////////////////////////////////////////////////////

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; )
            {
                // add UndoActions for all affected objects
                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();
            // make sure OrdNums are correct
            if (pSrcLst->IsObjOrdNumsDirty())
                pSrcLst->RecalcObjOrdNums();
            SdrObject*  pGrp=NULL;
            SdrObject*  pRefObj=NULL; // reference for InsertReason (-> anchors in Writer)
            SdrObject*  pRefObj1=NULL; // reference for InsertReason (-> anchors in Writer)
            SdrObjList* pDstLst=NULL;
            // if all selected objects come from foreign object lists.
            // the group object is the last one in the list.
            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,"Alleged group object doesn't return object list.");
                    }
                    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(); // this way, all ObjOrdNum of the page are set
                        nInsPos++;
                        bNeedInsPos=sal_False;
                    }
                    pSrcLst->RemoveObject(pObj->GetOrdNumDirect());
                    if (!bForeignList)
                        nInsPos--; // correct InsertPos
                    SdrInsertReason aReason(SDRREASON_VIEWCALL);
                    pDstLst->InsertObject(pObj,0,&aReason);
                    GetMarkedObjectListWriteAccess().DeleteMark(nm);
                    if (pRefObj1==NULL)
                        pRefObj1=pObj; // the topmost visible object
                    if (!bGrouped)
                    {
                        if (pRefObj==NULL)
                            pRefObj=pObj; // the topmost visible non-group object
                    }
                    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)); // no recalculation!
                    for (sal_uIntPtr no=0; no<nAnz; no++)
                    {
                        AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoInsertObject(*pDstLst->GetObj(no)));
                    }
                }
            }
        }
        GetMarkedObjectListWriteAccess().Merge(aNewMark);
        MarkListHasChanged();

        if( bUndo )
            EndUndo();
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Ungroup
////////////////////////////////////////////////////////////////////////////////////////////////////

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);  // retrieve name of group
                pGrp->TakeObjNamePlural(aName1); // retrieve name of group
                bNameOk=sal_True;
            } else {
                if (nCount==2) aName=aName1; // set plural name
                if (bNameOk) {
                    XubString aStr;
                    pGrp->TakeObjNamePlural(aStr); // retrieve name of group

                    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++;
                // No SortCheck when inserting into MarkList, because that would
                // provoke a RecalcOrdNums() each time because of pObj->GetOrdNum():
                aNewMark.InsertEntry(SdrMark(pObj,pM->GetPageView()),sal_False);
            }

            if( bUndo )
            {
                // Now it is safe to add the delete-UNDO which triggers the
                // MigrateItemPool now only for itself, not for the sub-objects.
                // 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); // Use the term "Group Objects," if different objects are grouped.
        SetUndoComment(ImpGetResStr(STR_EditUngroup),aName);
    }

    if( bUndo )
        EndUndo();

    if (nCount!=0)
    {
        GetMarkedObjectListWriteAccess().Merge(aNewMark,sal_True); // Because of the sorting above, aNewMark is reversed
        MarkListHasChanged();
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// ConvertToPoly
////////////////////////////////////////////////////////////////////////////////////////////////////

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: Object doesn't return object list");
        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;
    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();
                    ImpConvertOneObj(pObj,bPath,bLineToArea);
                }
            } else {
                SdrObject* pNewObj=ImpConvertOneObj(pObj,bPath,bLineToArea);
                if (pNewObj!=NULL) {
                    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);
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Metafile Import
////////////////////////////////////////////////////////////////////////////////////////////////////

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;)
    { // create Undo objects for all new objects
        // check for cancellation between the metafiles
        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;
        if (pGraf!=NULL && pGraf->HasGDIMetaFile())
        {
            ImpSdrGDIMetaFileImport aFilter(*pMod);
            aFilter.SetScaleRect(pGraf->GetSnapRect());
            aFilter.SetLayer(pObj->GetLayer());
            nInsAnz=aFilter.DoImport(pGraf->GetTransformedGraphic().GetGDIMetaFile(),*pOL,nInsPos,pProgrInfo);
        }
        if ( pOle2!=NULL && pOle2->GetGraphic() )
        {
            //const GDIMetaFile* pMtf=pOle2->GetGDIMetaFile();
            ImpSdrGDIMetaFileImport aFilter(*pMod);
            aFilter.SetScaleRect(pOle2->GetLogicRect());
            aFilter.SetLayer(pObj->GetLayer());
            nInsAnz=aFilter.DoImport(pOle2->GetGraphic()->GetGDIMetaFile(),*pOL,nInsPos,pProgrInfo);
        }
        if (nInsAnz!=0)
        {
            sal_uIntPtr nObj=nInsPos;
            for (sal_uIntPtr i=0; i<nInsAnz; i++)
            {
                if( bUndo )
                    AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pOL->GetObj(nObj)));

                // update new MarkList
                SdrMark aNewMark(pOL->GetObj(nObj), pPV);
                aNewMarked.InsertEntry(aNewMark);

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

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

            // remove object from selection and delete
            GetMarkedObjectListWriteAccess().DeleteMark(TryToFindMarkedObject(pObj));
            pOL->RemoveObject(nInsPos-1);

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

    if(aNewMarked.GetMarkCount())
    {
        // create new selection
        for(sal_uIntPtr a(0); a < aNewMarked.GetMarkCount(); a++)
        {
            GetMarkedObjectListWriteAccess().InsertEntry(*aNewMarked.GetMark(a));
        }

        SortMarkedObjects();
    }

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

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