Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/Delphi/Bille 0.71/__history/   (Columbo Version 0.7©)  Datei vom 9.0.2013 mit Größe 60 kB image not shown  

Quellcode-Bibliothek svdmrkv.cxx   Sprache: unbekannt

 
Columbo aufrufen.cxx Download desUnknown {[0] [0] [0]}Datei anzeigen

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */


#include <svx/svdmrkv.hxx>
#include <svx/svdview.hxx>
#include <svx/svdpagv.hxx>
#include <svx/svdpage.hxx>
#include <svx/svdotable.hxx>
#include <svx/svdomedia.hxx>

#include <osl/diagnose.h>
#include <osl/thread.h>
#include <rtl/strbuf.hxx>
#include <svx/svdoole2.hxx>
#include <svx/xfillit0.hxx>
#include <svx/xflgrit.hxx>
#include "gradtrns.hxx"
#include <svx/xflftrit.hxx>
#include <svx/dialmgr.hxx>
#include <svx/strings.hrc>
#include <svx/svdundo.hxx>
#include <svx/svdopath.hxx>
#include <svx/scene3d.hxx>
#include <svx/svdovirt.hxx>
#include <sdr/overlay/overlayrollingrectangle.hxx>
#include <svx/sdr/overlay/overlaypolypolygon.hxx>
#include <svx/sdr/contact/displayinfo.hxx>
#include <svx/sdr/contact/objectcontact.hxx>
#include <svx/sdr/overlay/overlaymanager.hxx>
#include <svx/sdr/overlay/overlayselection.hxx>
#include <svx/sdr/contact/viewcontact.hxx>
#include <svx/sdr/contact/viewobjectcontact.hxx>
#include <svx/sdrpaintwindow.hxx>
#include <svx/sdrpagewindow.hxx>
#include <svx/sdrhittesthelper.hxx>
#include <vcl/uitest/logger.hxx>
#include <vcl/uitest/eventdescription.hxx>
#include <vcl/window.hxx>
#include <o3tl/string_view.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>

#include <LibreOfficeKit/LibreOfficeKitEnums.h>
#include <comphelper/lok.hxx>
#include <sfx2/lokhelper.hxx>
#include <sfx2/lokcomponenthelpers.hxx>
#include <sfx2/viewsh.hxx>
#include <sfx2/objsh.hxx>
#include <svtools/optionsdrawinglayer.hxx>

#include <drawinglayer/processor2d/textextractor2d.hxx>
#include <svl/cryptosign.hxx>

#include <array>

#include <com/sun/star/frame/XController.hpp>
#include <com/sun/star/view/XSelectionSupplier.hpp>

#include <boost/property_tree/json_parser.hpp>

using namespace com::sun::star;

// Migrate Marking of Objects, Points and GluePoints

class ImplMarkingOverlay
{
    // The OverlayObjects
    sdr::overlay::OverlayObjectList               maObjects;

    // The remembered second position in logical coordinates
    basegfx::B2DPoint                               maSecondPosition;

    // A flag to remember if the action is for unmarking.
    bool                                            mbUnmarking : 1;

public:
    ImplMarkingOverlay(const SdrPaintView& rView, const basegfx::B2DPoint& rStartPos, bool bUnmarking);

    // The OverlayObjects are cleared using the destructor of OverlayObjectList.
    // That destructor calls clear() at the list which removes all objects from the
    // OverlayManager and deletes them.

    void SetSecondPosition(const basegfx::B2DPoint& rNewPosition);
    bool IsUnmarking() const { return mbUnmarking; }
};

ImplMarkingOverlay::ImplMarkingOverlay(const SdrPaintView& rView, const basegfx::B2DPoint& rStartPos, bool bUnmarking)
:   maSecondPosition(rStartPos),
    mbUnmarking(bUnmarking)
{
    if (comphelper::LibreOfficeKit::isActive())
        return; // We do client-side object manipulation with the Kit API

    for(sal_uInt32 a(0); a < rView.PaintWindowCount(); a++)
    {
        SdrPaintWindow* pCandidate = rView.GetPaintWindow(a);
        const rtl::Reference< sdr::overlay::OverlayManager >& xTargetOverlay = pCandidate->GetOverlayManager();

        if (xTargetOverlay.is())
        {
            std::unique_ptr<sdr::overlay::OverlayRollingRectangleStriped> pNew(new sdr::overlay::OverlayRollingRectangleStriped(
                rStartPos, rStartPos, false));
            xTargetOverlay->add(*pNew);
            maObjects.append(std::move(pNew));
        }
    }
}

void ImplMarkingOverlay::SetSecondPosition(const basegfx::B2DPoint& rNewPosition)
{
    if(rNewPosition != maSecondPosition)
    {
        // apply to OverlayObjects
        for(sal_uInt32 a(0); a < maObjects.count(); a++)
        {
            sdr::overlay::OverlayRollingRectangleStriped& rCandidate = static_cast< sdr::overlay::OverlayRollingRectangleStriped&>(maObjects.getOverlayObject(a));
            rCandidate.setSecondPosition(rNewPosition);
        }

        // remember new position
        maSecondPosition = rNewPosition;
    }
}

class MarkingSelectionOverlay
{
    sdr::overlay::OverlayObjectList maObjects;
public:
    MarkingSelectionOverlay(const SdrPaintView& rView, basegfx::B2DRectangle const&&nbsp;rSelection)
    {
        if (comphelper::LibreOfficeKit::isActive())
            return; // We do client-side object manipulation with the Kit API

        for (sal_uInt32 a(0); a < rView.PaintWindowCount(); a++)
        {
            SdrPaintWindow* pPaintWindow = rView.GetPaintWindow(a);
            const rtl::Reference<sdr::overlay::OverlayManager>& xTargetOverlay = pPaintWindow->GetOverlayManager();

            if (xTargetOverlay.is())
            {
                basegfx::B2DPolyPolygon aPolyPoly(basegfx::utils::createPolygonFromRect(rSelection));
                auto pNew = std::make_unique<sdr::overlay::OverlayPolyPolygon>(aPolyPoly, COL_GRAY, 0, COL_TRANSPARENT);
                xTargetOverlay->add(*pNew);
                maObjects.append(std::move(pNew));
            }
        }
    }
};

class MarkingSubSelectionOverlay
{
    sdr::overlay::OverlayObjectList maObjects;

public:
    MarkingSubSelectionOverlay(const SdrPaintView& rView, std::vector<basegfx::B2DRectangle> const & rSelections)
    {
        if (comphelper::LibreOfficeKit::isActive())
            return; // We do client-side object manipulation with the Kit API

        for (sal_uInt32 a(0); a < rView.PaintWindowCount(); a++)
        {
            SdrPaintWindow* pCandidate = rView.GetPaintWindow(a);
            const rtl::Reference<sdr::overlay::OverlayManager>& xTargetOverlay = pCandidate->GetOverlayManager();

            if (xTargetOverlay.is())
            {
                const Color aHighlightColor = SvtOptionsDrawinglayer::getHilightColor();

                std::unique_ptr<sdr::overlay::OverlaySelection> pNew =
                    std::make_unique<sdr::overlay::OverlaySelection>(
                        sdr::overlay::OverlayType::Transparent,
                        aHighlightColor, std::vector(rSelections), false);

                xTargetOverlay->add(*pNew);
                maObjects.append(std::move(pNew));
            }
        }
    }
};

SdrMarkView::SdrMarkView(SdrModel& rSdrModel, OutputDevice* pOut)
    : SdrSnapView(rSdrModel, pOut)
    , mpMarkedObj(nullptr)
    , mpMarkedPV(nullptr)
    , maHdlList(this)
    , meDragMode(SdrDragMode::Move)
    , meEditMode(SdrViewEditMode::Edit)
    , meEditMode0(SdrViewEditMode::Edit)
    , mbDesignMode(false)
    , mbForceFrameHandles(false)
    , mbPlusHdlAlways(false)
    , mbInsPolyPoint(false)
    , mbMarkedObjRectDirty(false)
    , mbMrkPntDirty(false)
    , mbMarkedPointsRectsDirty(false)
    , mbMarkHandlesHidden(false)
    , mbNegativeX(false)
{

    BrkMarkObj();
    BrkMarkPoints();
    BrkMarkGluePoints();

    StartListening(rSdrModel);
}

SdrMarkView::~SdrMarkView()
{
    // Migrate selections
    BrkMarkObj();
    BrkMarkPoints();
    BrkMarkGluePoints();
}

void SdrMarkView::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
{
    if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint)
    {
        const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint);
        SdrHintKind eKind=pSdrHint->GetKind();
        if (eKind==SdrHintKind::ObjectChange || eKind==SdrHintKind::ObjectInserted || eKind==SdrHintKind::ObjectRemoved)
        {
            mbMarkedObjRectDirty=true;
            mbMarkedPointsRectsDirty=true;
        }
    }
    SdrSnapView::Notify(rBC,rHint);
}

void SdrMarkView::ModelHasChanged()
{
    SdrPaintView::ModelHasChanged();
    GetMarkedObjectListWriteAccess().SetNameDirty();
    mbMarkedObjRectDirty=true;
    mbMarkedPointsRectsDirty=true;
    // Example: Obj is selected and maMarkedObjectList is sorted.
    // In another View 2, the ObjOrder is changed (e. g. MovToTop())
    // Then we need to re-sort MarkList.
    GetMarkedObjectListWriteAccess().SetUnsorted();
    const SdrMarkList& rMarkList = GetMarkedObjectList();
    rMarkList.ForceSort();
    mbMrkPntDirty=true;
    UndirtyMrkPnt();
    SdrView* pV=static_cast<SdrView*>(this);
    if (!pV->IsDragObj() && !pV->IsInsObjPoint()) {
        AdjustMarkHdl();
    }

    if (comphelper::LibreOfficeKit::isActive())
        modelHasChangedLOKit();
}

void SdrMarkView::modelHasChangedLOKit()
{
    const SdrMarkList& rMarkList = GetMarkedObjectList();
    if (rMarkList.GetMarkCount() <= 0)
        return;

    //TODO: Is MarkedObjRect valid at this point?
    tools::Rectangle aSelection(GetMarkedObjRect());
    tools::Rectangle* pResultSelection;
    if (aSelection.IsEmpty())
        pResultSelection = nullptr;
    else
    {
        sal_uInt32 nTotalPaintWindows = this->PaintWindowCount();
        if (nTotalPaintWindows == 1)
        {
            const OutputDevice* pOut = this->GetFirstOutputDevice();
            const vcl::Window* pWin = pOut ? pOut->GetOwnerWindow() : nullptr;
            if (pWin && pWin->IsChart())
            {
                if (SfxViewShell* pViewShell = GetSfxViewShell())
                {
                    const vcl::Window* pViewShellWindow = pViewShell->GetEditWindowForActiveOLEObj();
                    if (pViewShellWindow && pViewShellWindow->IsAncestorOf(*pWin))
                    {
                        Point aOffsetPx = pWin->GetOffsetPixelFrom(*pViewShellWindow);
                        Point aLogicOffset = pWin->PixelToLogic(aOffsetPx);
                        aSelection.Move(aLogicOffset.getX(), aLogicOffset.getY());
                    }
                }
            }
        }

        // In case the map mode is in 100th MM, then need to convert the coordinates over to twips for LOK.
        if (mpMarkedPV)
        {
            if (OutputDevice* pOutputDevice = mpMarkedPV->GetView().GetFirstOutputDevice())
            {
                if (pOutputDevice->GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
                    aSelection = o3tl::convert(aSelection, o3tl::Length::mm100, o3tl::Length::twip);
            }
        }

        pResultSelection = &aSelection;

        if (mbNegativeX)
        {
            // Convert to positive X doc-coordinates
            tools::Long nTmp = aSelection.Left();
            aSelection.SetLeft(-aSelection.Right());
            aSelection.SetRight(-nTmp);
        }
    }

    if (SfxViewShell* pViewShell = GetSfxViewShell())
        SfxLokHelper::notifyInvalidation(pViewShell, pResultSelection);
}

bool SdrMarkView::IsAction() const
{
    return SdrSnapView::IsAction() || IsMarkObj() || IsMarkPoints() || IsMarkGluePoints();
}

void SdrMarkView::MovAction(const Point& rPnt)
{
    SdrSnapView::MovAction(rPnt);

    if(IsMarkObj())
    {
        MovMarkObj(rPnt);
    }
    else if(IsMarkPoints())
    {
        MovMarkPoints(rPnt);
    }
    else if(IsMarkGluePoints())
    {
        MovMarkGluePoints(rPnt);
    }
}

void SdrMarkView::EndAction()
{
    if(IsMarkObj())
    {
        EndMarkObj();
    }
    else if(IsMarkPoints())
    {
        EndMarkPoints();
    }
    else if(IsMarkGluePoints())
    {
        EndMarkGluePoints();
    }

    SdrSnapView::EndAction();
}

void SdrMarkView::BckAction()
{
    SdrSnapView::BckAction();
    BrkMarkObj();
    BrkMarkPoints();
    BrkMarkGluePoints();
}

void SdrMarkView::BrkAction()
{
    SdrSnapView::BrkAction();
    BrkMarkObj();
    BrkMarkPoints();
    BrkMarkGluePoints();
}

void SdrMarkView::TakeActionRect(tools::Rectangle& rRect) const
{
    if(IsMarkObj() || IsMarkPoints() || IsMarkGluePoints())
    {
        rRect = tools::Rectangle(maDragStat.GetStart(), maDragStat.GetNow());
    }
    else
    {
        SdrSnapView::TakeActionRect(rRect);
    }
}


void SdrMarkView::ClearPageView()
{
    UnmarkAllObj();
    SdrSnapView::ClearPageView();
}

void SdrMarkView::HideSdrPage()
{
    bool bMrkChg(false);

    SdrPageView* pPageView = GetSdrPageView();
    if (pPageView)
    {
        // break all creation actions when hiding page (#75081#)
        BrkAction();

        // Discard all selections on this page
        bMrkChg = GetMarkedObjectListWriteAccess().DeletePageView(*pPageView);
    }

    SdrSnapView::HideSdrPage();

    if(bMrkChg)
    {
        MarkListHasChanged();
        AdjustMarkHdl();
    }
}


void SdrMarkView::BegMarkObj(const Point& rPnt, bool bUnmark)
{
    BrkAction();

    DBG_ASSERT(!mpMarkObjOverlay, "SdrMarkView::BegMarkObj: There exists a mpMarkObjOverlay (!)");

    basegfx::B2DPoint aStartPos(rPnt.X(), rPnt.Y());
    mpMarkObjOverlay.reset(new ImplMarkingOverlay(*this, aStartPos, bUnmark));

    maDragStat.Reset(rPnt);
    maDragStat.NextPoint();
    maDragStat.SetMinMove(mnMinMovLog);
}

void SdrMarkView::MovMarkObj(const Point& rPnt)
{
    if(IsMarkObj() && maDragStat.CheckMinMoved(rPnt))
    {
        maDragStat.NextMove(rPnt);
        DBG_ASSERT(mpMarkObjOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
        basegfx::B2DPoint aNewPos(rPnt.X(), rPnt.Y());
        mpMarkObjOverlay->SetSecondPosition(aNewPos);
    }
}

bool SdrMarkView::EndMarkObj()
{
    bool bRetval(false);

    if(IsMarkObj())
    {
        if(maDragStat.IsMinMoved())
        {
            tools::Rectangle aRect(maDragStat.GetStart(), maDragStat.GetNow());
            aRect.Normalize();
            MarkObj(aRect, mpMarkObjOverlay->IsUnmarking());
            bRetval = true;
        }

        // cleanup
        BrkMarkObj();
    }

    return bRetval;
}

void SdrMarkView::BrkMarkObj()
{
    if(IsMarkObj())
    {
        DBG_ASSERT(mpMarkObjOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
        mpMarkObjOverlay.reset();
    }
}


bool SdrMarkView::BegMarkPoints(const Point& rPnt, bool bUnmark)
{
    if(HasMarkablePoints())
    {
        BrkAction();

        DBG_ASSERT(!mpMarkPointsOverlay, "SdrMarkView::BegMarkObj: There exists a mpMarkPointsOverlay (!)");
        basegfx::B2DPoint aStartPos(rPnt.X(), rPnt.Y());
        mpMarkPointsOverlay.reset(new ImplMarkingOverlay(*this, aStartPos, bUnmark));

        maDragStat.Reset(rPnt);
        maDragStat.NextPoint();
        maDragStat.SetMinMove(mnMinMovLog);

        return true;
    }

    return false;
}

void SdrMarkView::MovMarkPoints(const Point& rPnt)
{
    if(IsMarkPoints() && maDragStat.CheckMinMoved(rPnt))
    {
        maDragStat.NextMove(rPnt);

        DBG_ASSERT(mpMarkPointsOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
        basegfx::B2DPoint aNewPos(rPnt.X(), rPnt.Y());
        mpMarkPointsOverlay->SetSecondPosition(aNewPos);
    }
}

bool SdrMarkView::EndMarkPoints()
{
    bool bRetval(false);

    if(IsMarkPoints())
    {
        if(maDragStat.IsMinMoved())
        {
            tools::Rectangle aRect(maDragStat.GetStart(), maDragStat.GetNow());
            aRect.Normalize();
            MarkPoints(&aRect, mpMarkPointsOverlay->IsUnmarking());

            bRetval = true;
        }

        // cleanup
        BrkMarkPoints();
    }

    return bRetval;
}

void SdrMarkView::BrkMarkPoints()
{
    if(IsMarkPoints())
    {
        DBG_ASSERT(mpMarkPointsOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
        mpMarkPointsOverlay.reset();
    }
}


bool SdrMarkView::BegMarkGluePoints(const Point& rPnt, bool bUnmark)
{
    if(HasMarkableGluePoints())
    {
        BrkAction();

        DBG_ASSERT(!mpMarkGluePointsOverlay, "SdrMarkView::BegMarkObj: There exists a mpMarkGluePointsOverlay (!)");

        basegfx::B2DPoint aStartPos(rPnt.X(), rPnt.Y());
        mpMarkGluePointsOverlay.reset(new ImplMarkingOverlay(*this, aStartPos, bUnmark));
        maDragStat.Reset(rPnt);
        maDragStat.NextPoint();
        maDragStat.SetMinMove(mnMinMovLog);

        return true;
    }

    return false;
}

void SdrMarkView::MovMarkGluePoints(const Point& rPnt)
{
    if(IsMarkGluePoints() && maDragStat.CheckMinMoved(rPnt))
    {
        maDragStat.NextMove(rPnt);

        DBG_ASSERT(mpMarkGluePointsOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
        basegfx::B2DPoint aNewPos(rPnt.X(), rPnt.Y());
        mpMarkGluePointsOverlay->SetSecondPosition(aNewPos);
    }
}

void SdrMarkView::EndMarkGluePoints()
{
    if(IsMarkGluePoints())
    {
        if(maDragStat.IsMinMoved())
        {
            tools::Rectangle aRect(maDragStat.GetStart(),maDragStat.GetNow());
            aRect.Normalize();
            MarkGluePoints(&aRect, mpMarkGluePointsOverlay->IsUnmarking());
        }

        // cleanup
        BrkMarkGluePoints();
    }
}

void SdrMarkView::BrkMarkGluePoints()
{
    if(IsMarkGluePoints())
    {
        DBG_ASSERT(mpMarkGluePointsOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
        mpMarkGluePointsOverlay.reset();
    }
}

bool SdrMarkView::MarkableObjectsExceed( int n ) const
{
    SdrPageView* pPV = GetSdrPageView();
    if (!pPV)
        return false;

    SdrObjList* pOL=pPV->GetObjList();
    for (const rtl::Reference<SdrObject>& pObj : *pOL)
        if (IsObjMarkable(pObj.get(),pPV) && --n<0)
            return true;

    return false;
}

void SdrMarkView::hideMarkHandles()
{
    if(!mbMarkHandlesHidden)
    {
        mbMarkHandlesHidden = true;
        AdjustMarkHdl();
    }
}

void SdrMarkView::showMarkHandles()
{
    if(mbMarkHandlesHidden)
    {
        mbMarkHandlesHidden = false;
        AdjustMarkHdl();
    }
}

bool SdrMarkView::ImpIsFrameHandles() const
{
    const SdrMarkList& rMarkList = GetMarkedObjectList();
    const size_t nMarkCount=rMarkList.GetMarkCount();
    bool bFrmHdl=nMarkCount>static_cast<size_t>(mnFrameHandlesLimit) || mbForceFrameHandles;
    bool bStdDrag=meDragMode==SdrDragMode::Move;
    if (nMarkCount==1 && bStdDrag && bFrmHdl)
    {
        const SdrObject* pObj=rMarkList.GetMark(0)->GetMarkedSdrObj();
        if (pObj && pObj->GetObjInventor()==SdrInventor::Default)
        {
            SdrObjKind nIdent=pObj->GetObjIdentifier();
            if (nIdent==SdrObjKind::Line || nIdent==SdrObjKind::Edge || nIdent==SdrObjKind::Caption || nIdent==SdrObjKind::Measure || nIdent==SdrObjKind::CustomShape || nIdent==SdrObjKind::Table )
            {
                bFrmHdl=false;
            }
        }
    }
    if (!bStdDrag && !bFrmHdl) {
        // all other drag modes only with FrameHandles
        bFrmHdl=true;
        if (meDragMode==SdrDragMode::Rotate) {
            // when rotating, use ObjOwn drag, if there's at least 1 PolyObj
            for (size_t nMarkNum=0; nMarkNum<nMarkCount && bFrmHdl; ++nMarkNum) {
                const SdrMark* pM=rMarkList.GetMark(nMarkNum);
                const SdrObject* pObj=pM->GetMarkedSdrObj();
                bFrmHdl=!pObj->IsPolyObj();
            }
        }
    }
    if (!bFrmHdl) {
        // FrameHandles, if at least 1 Obj can't do SpecialDrag
        for (size_t nMarkNum=0; nMarkNum<nMarkCount && !bFrmHdl; ++nMarkNum) {
            const SdrMark* pM=rMarkList.GetMark(nMarkNum);
            const SdrObject* pObj=pM->GetMarkedSdrObj();
            bFrmHdl=!pObj->hasSpecialDrag();
        }
    }

    // no FrameHdl for crop
    if(bFrmHdl && SdrDragMode::Crop == meDragMode)
    {
        bFrmHdl = false;
    }

    return bFrmHdl;
}

namespace
{
std::u16string_view lcl_getDragMethodServiceName( std::u16string_view rCID )
{
    std::u16string_view aRet;

    size_t nIndexStart = rCID.find( u"DragMethod=" );
    if( nIndexStart != std::u16string_view::npos )
    {
        nIndexStart = rCID.find( '=', nIndexStart );
        if( nIndexStart != std::u16string_view::npos )
        {
            nIndexStart++;
            size_t nNextSlash = rCID.find( '/', nIndexStart );
            if( nNextSlash != std::u16string_view::npos )
            {
                sal_Int32 nIndexEnd = nNextSlash;
                size_t nNextColon = rCID.find( ':', nIndexStart );
                if( nNextColon == std::u16string_view::npos || nNextColon < nNextSlash )
                    nIndexEnd = nNextColon;
                aRet = rCID.substr(nIndexStart,nIndexEnd-nIndexStart);
            }
        }
    }
    return aRet;
}

std::u16string_view lcl_getDragParameterString( std::u16string_view rCID )
{
    std::u16string_view aRet;

    size_t nIndexStart = rCID.find( u"DragParameter=" );
    if( nIndexStart != std::u16string_view::npos )
    {
        nIndexStart = rCID.find( '=', nIndexStart );
        if( nIndexStart != std::u16string_view::npos )
        {
            nIndexStart++;
            size_t nNextSlash = rCID.find( '/', nIndexStart );
            if( nNextSlash != std::u16string_view::npos )
            {
                sal_Int32 nIndexEnd = nNextSlash;
                size_t nNextColon = rCID.find( ':', nIndexStart );
                if( nNextColon == std::u16string_view::npos || nNextColon < nNextSlash )
                    nIndexEnd = nNextColon;
                aRet = rCID.substr(nIndexStart,nIndexEnd-nIndexStart);
            }
        }
    }
    return aRet;
}
} // anonymous namespace

bool SdrMarkView::dumpGluePointsToJSON(boost::property_tree::ptree& rTree)
{
    bool result = false;
    tools::Long nSignX = mbNegativeX ? -1 : 1;
    if (OutputDevice* pOutDev = mpMarkedPV ? mpMarkedPV->GetView().GetFirstOutputDevice() : nullptr)
    {
        bool bConvertUnit = false;
        if (pOutDev->GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
            bConvertUnit = true;
        const SdrObjList* pOL = mpMarkedPV->GetObjList();
        if (!pOL)
            return false;
        boost::property_tree::ptree elements;
        const SdrMarkList& rMarkList = GetMarkedObjectList();
        for (const rtl::Reference<SdrObject>& pObj : *pOL)
        {
            if (!pObj)
                continue;
            if (pObj == rMarkList.GetMark(0)->GetMarkedSdrObj())
                continue;
            const SdrGluePointList* pGPL = pObj->GetGluePointList();
            bool VertexObject = !(pGPL && pGPL->GetCount());
            const size_t count = !VertexObject ? pGPL->GetCount() : 4;
            boost::property_tree::ptree object;
            boost::property_tree::ptree points;
            for (size_t i = 0; i < count; ++i)
            {
                boost::property_tree::ptree node;
                boost::property_tree::ptree point;
                const SdrGluePoint aGP = !VertexObject ? (*pGPL)[i] : pObj->GetVertexGluePoint(i);
                Point rPoint = aGP.GetAbsolutePos(*pObj);
                if (bConvertUnit)
                {
                    rPoint = o3tl::convert(rPoint, o3tl::Length::mm100, o3tl::Length::twip);
                }
                point.put("x", nSignX * rPoint.getX());
                point.put("y", rPoint.getY());
                node.add_child("point", point);
                points.push_back(std::make_pair("", node));
            }
            basegfx::B2DVector aGridOffset(0.0, 0.0);
            Point objLogicRectTopLeft = pObj->GetLogicRect().TopLeft();
            if(getPossibleGridOffsetForPosition(aGridOffset, basegfx::B2DPoint(objLogicRectTopLeft.X(), objLogicRectTopLeft.Y()), GetSdrPageView()))
            {
                Point p(aGridOffset.getX(), aGridOffset.getY());
                if (bConvertUnit)
                {
                    p = o3tl::convert(p, o3tl::Length::mm100, o3tl::Length::twip);
                }
                boost::property_tree::ptree gridOffset;
                gridOffset.put("x", nSignX * p.getX());
                gridOffset.put("y", p.getY());
                object.add_child("gridoffset", gridOffset);
            }
            object.put("ordnum", pObj->GetOrdNum());
            object.add_child("gluepoints", points);
            elements.push_back(std::make_pair("", object));
            result = true;
        }
        rTree.add_child("shapes", elements);
    }
    return result;
}

namespace
{
    class TextBoundsExtractor final : public drawinglayer::processor2d::TextExtractor2D
    {
    private:
        basegfx::B2DRange maTextRange;
        void processTextPrimitive2D(const drawinglayer::primitive2d::BasePrimitive2D& rCandidate) override
        {
            maTextRange.expand(rCandidate.getB2DRange(getViewInformation2D()));
        }
    public:
        explicit TextBoundsExtractor(const drawinglayer::geometry::ViewInformation2D& rViewInformation)
            : drawinglayer::processor2d::TextExtractor2D(rViewInformation)
        {
        }

        const basegfx::B2DRange & getTextBounds(const sdr::contact::ViewObjectContact &rVOC,&nbsp;const sdr::contact::DisplayInfo &raDisplayInfo)
        {
            this->process(rVOC.getPrimitive2DSequence(raDisplayInfo));
            return maTextRange;
        }
    };
}

OString SdrMarkView::CreateInnerTextRectString() const
{
    if (!mpMarkedObj)
        return OString();

    SdrPageView* pPageView = GetSdrPageView();
    const sdr::contact::ViewObjectContact& rVOC = mpMarkedObj->GetViewContact().GetViewObjectContact(
        pPageView->GetPageWindow(0)->GetObjectContact());

    sdr::contact::DisplayInfo aDisplayInfo;
    TextBoundsExtractor aTextBoundsExtractor(rVOC.GetObjectContact().getViewInformation2D());
    basegfx::B2DRange aRange = aTextBoundsExtractor.getTextBounds(rVOC, aDisplayInfo);
    if (!aRange.isEmpty()) {
        tools::Rectangle rect(aRange.getMinX(), aRange.getMinY(), aRange.getMaxX(), aRange.getMaxY());
        tools::Rectangle aRangeTWIP = o3tl::convert(rect, o3tl::Length::mm100, o3tl::Length::twip);
        OString innerTextInfo = "\"innerTextRect\":[" +
            OString::number(aRangeTWIP.getX()) + "," +
            OString::number(aRangeTWIP.getY()) + "," +
            OString::number(aRangeTWIP.GetWidth()) + "," +
            OString::number(aRangeTWIP.GetHeight()) + "]";
        return innerTextInfo;
    }

    return OString();
}

void SdrMarkView::SetInnerTextAreaForLOKit() const
{
    if (!comphelper::LibreOfficeKit::isActive())
        return;
    SfxViewShell* pViewShell = GetSfxViewShell();
    OString sRectString = CreateInnerTextRectString();
    if (pViewShell && !sRectString.isEmpty())
        pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_SHAPE_INNER_TEXT, sRectString);
}

void SdrMarkView::SetMarkHandlesForLOKit(tools::Rectangle const & rRect, const SfxViewShell* pOtherShell)
{
    SfxViewShell* pViewShell = GetSfxViewShell();

    tools::Rectangle aSelection(rRect);
    tools::Long nSignX = mbNegativeX ? -1 : 1;
    bool bIsChart = false;
    Point addLogicOffset(0, 0);
    bool convertMapMode = false;
    if (!rRect.IsEmpty())
    {
        sal_uInt32 nTotalPaintWindows = this->PaintWindowCount();
        if (nTotalPaintWindows == 1)
        {
            const OutputDevice* pOut = this->GetFirstOutputDevice();
            const vcl::Window* pWin = pOut ? pOut->GetOwnerWindow() : nullptr;
            if (pWin && pWin->IsChart())
            {
                bIsChart = true;
                const vcl::Window* pViewShellWindow = GetSfxViewShell()->GetEditWindowForActiveOLEObj();
                if (pViewShellWindow && pViewShellWindow->IsAncestorOf(*pWin))
                {
                    Point aOffsetPx = pWin->GetOffsetPixelFrom(*pViewShellWindow);
                    if (mbNegativeX && AllSettings::GetLayoutRTL())
                    {
                        // mbNegativeX is set only for Calc in RTL mode.
                        // If global RTL flag is set, vcl-window X offset of chart window is
                        // mirrored w.r.t parent window rectangle. This needs to be reverted.
                        aOffsetPx.setX(pViewShellWindow->GetOutOffXPixel() + pViewShellWindow->GetSizePixel().Width()
                            - pWin->GetOutOffXPixel() - pWin->GetSizePixel().Width());
                    }
                    Point aLogicOffset = pWin->PixelToLogic(aOffsetPx);
                    addLogicOffset = aLogicOffset;
                    aSelection.Move(aLogicOffset.getX(), aLogicOffset.getY());
                }
            }
        }
    }

    if (!aSelection.IsEmpty())
    {
        // In case the map mode is in 100th MM, then need to convert the coordinates over to twips for LOK.
        if (mpMarkedPV)
        {
            if (OutputDevice* pOutputDevice = mpMarkedPV->GetView().GetFirstOutputDevice())
            {
                if (pOutputDevice->GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
                {
                    aSelection = o3tl::convert(aSelection, o3tl::Length::mm100, o3tl::Length::twip);
                    convertMapMode = true;
                }
            }
        }

        // hide the text selection too
        if (pViewShell)
            pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, ""_ostr);
    }

    {
        OStringBuffer aExtraInfo;
        OString sSelectionText;
        OString sSelectionTextView;
        boost::property_tree::ptree aTableJsonTree;
        boost::property_tree::ptree aGluePointsTree;
        const bool bMediaObj = (mpMarkedObj && mpMarkedObj->GetObjIdentifier() == SdrObjKind::Media);
        bool bTableSelection = false;
        bool bConnectorSelection = false;

        if (mpMarkedObj && mpMarkedObj->GetObjIdentifier() == SdrObjKind::Table)
        {
            auto& rTableObject = dynamic_cast<sdr::table::SdrTableObj&>(*mpMarkedObj);
            bTableSelection = rTableObject.createTableEdgesJson(aTableJsonTree);
        }
        if (mpMarkedObj && mpMarkedObj->GetObjIdentifier() == SdrObjKind::Edge)
        {
            bConnectorSelection = dumpGluePointsToJSON(aGluePointsTree);
        }

        SdrPageView* pPageView = GetSdrPageView();

        const SdrMarkList& rMarkList = GetMarkedObjectList();
        if (rMarkList.GetMarkCount())
        {
            SdrMark* pM = rMarkList.GetMark(0);
            SdrObject* pO = pM->GetMarkedSdrObj();
            Degree100 nRotAngle = pO->GetRotateAngle();
            // true if we are dealing with a RotGrfFlyFrame
            // (SwVirtFlyDrawObj with a SwGrfNode)
            bool bWriterGraphic = pO->HasLimitedRotation();

            OString handleArrayStr;

            aExtraInfo.append("{\"id\":\""
                + OString::number(reinterpret_cast<sal_IntPtr>(pO))
                + "\",\"type\":"
                + OString::number(static_cast<sal_Int32>(pO->GetObjIdentifier()))
                + ",\"OrdNum\":" + OString::number(pO->GetOrdNum())
                );

            if (mpMarkedObj && !pOtherShell)
            {
                OString innerTextInfo = CreateInnerTextRectString();
                if (!innerTextInfo.isEmpty())
                    aExtraInfo.append("," + innerTextInfo);
            }

            // In core, the gridOffset is calculated based on the LogicRect's TopLeft coordinate
            // In online, we have the SnapRect and we calculate it based on its TopLeft coordinate
            // SnapRect's TopLeft and LogicRect's TopLeft match unless there is rotation
            // but the rotation is not applied to the LogicRect. Therefore,
            // what we calculate in online does not match with the core in case of the rotation.
            // Here we can send the correct gridOffset in the selection callback, this way
            // whether the shape is rotated or not, we will always have the correct gridOffset
            // Note that the gridOffset is calculated from the first selected obj
            basegfx::B2DVector aGridOffset(0.0, 0.0);
            if(getPossibleGridOffsetForSdrObject(aGridOffset, rMarkList.GetMark(0)->GetMarkedSdrObj(), pPageView))
            {
                Point p(aGridOffset.getX(), aGridOffset.getY());
                if (convertMapMode)
                    p = o3tl::convert(p, o3tl::Length::mm100, o3tl::Length::twip);
                aExtraInfo.append(",\"gridOffsetX\":"
                    + OString::number(nSignX * p.getX())
                    + ",\"gridOffsetY\":"
                    + OString::number(p.getY()));
            }

            if (meDragMode == SdrDragMode::Crop)
                aExtraInfo.append(", \"isCropMode\": true");

            if (bWriterGraphic)
            {
                aExtraInfo.append(", \"isWriterGraphic\": true");
            }
            else if (bIsChart)
            {
                LokChartHelper aChartHelper(pViewShell);
                css::uno::Reference<css::frame::XController>& xChartController = aChartHelper.GetXController();
                css::uno::Reference<css::view::XSelectionSupplier> xSelectionSupplier( xChartController, uno::UNO_QUERY);
                if (xSelectionSupplier.is())
                {
                    uno::Any aSel = xSelectionSupplier->getSelection();
                    OUString aValue;
                    if (aSel >>= aValue)
                    {
                        OString aObjectCID(aValue.getStr(), aValue.getLength(), osl_getThreadTextEncoding());
                        const std::vector<OString> aProps{"Draggable"_ostr, "Resizable"_ostr, "Rotatable"_ostr};
                        for (const auto& rProp: aProps)
                        {
                            sal_Int32 nPos = aObjectCID.indexOf(rProp);
                            if (nPos == -1) continue;
                            nPos += rProp.getLength() + 1; // '='
                            if (aExtraInfo.getLength() > 2) // != "{ "
                                aExtraInfo.append(", ");
                            aExtraInfo.append("\"is" + rProp + "\": "
                                + OString::boolean(aObjectCID[nPos] == '1'));
                        }

                        std::u16string_view sDragMethod = lcl_getDragMethodServiceName(aValue);
                        if (sDragMethod == u"PieSegmentDragging")
                        {
                            // old initial offset inside the CID returned by xSelectionSupplier->getSelection()
                            // after a pie segment dragging; using SdrObject::GetName for getting a CID with the updated offset
                            aValue = pO->GetName();
                            std::u16string_view sDragParameters = lcl_getDragParameterString(aValue);
                            if (!sDragParameters.empty())
                            {
                                aExtraInfo.append(", \"dragInfo\": { "
                                    "\"dragMethod\": \""
                                    + OUString(sDragMethod).toUtf8()
                                    + "\"");

                                sal_Int32 nStartIndex = 0;
                                std::array<int, 5> aDragParameters;
                                for (auto& rParam : aDragParameters)
                                {
                                    std::u16string_view sParam = o3tl::getToken(sDragParameters, 0, ',', nStartIndex);
                                    if (sParam.empty())
                                        break;
                                    rParam = o3tl::toInt32(sParam);
                                }

                                // initial offset in %
                                if (aDragParameters[0] < 0)
                                    aDragParameters[0] = 0;
                                else if (aDragParameters[0] > 100)
                                    aDragParameters[0] = 100;

                                aExtraInfo.append(", \"initialOffset\": "
                                    + OString::number(static_cast<sal_Int32>(aDragParameters[0])));

                                // drag direction constraint
                                Point aMinPos(aDragParameters[1], aDragParameters[2]);
                                Point aMaxPos(aDragParameters[3], aDragParameters[4]);
                                Point aDragDirection = aMaxPos - aMinPos;
                                aDragDirection = o3tl::convert(aDragDirection, o3tl::Length::mm100, o3tl::Length::twip);

                                aExtraInfo.append(", \"dragDirection\": ["
                                    + aDragDirection.toString()
                                    + "]");

                                // polygon approximating the pie segment or donut segment
                                if (pViewShell && pO->GetObjIdentifier() == SdrObjKind::PathFill)
                                {
                                    const basegfx::B2DPolyPolygon aPolyPolygon(pO->TakeXorPoly());
                                    if (aPolyPolygon.count() == 1)
                                    {
                                        const basegfx::B2DPolygon& aPolygon = aPolyPolygon.getB2DPolygon(0);
                                        if (sal_uInt32 nPolySize = aPolygon.count())
                                        {
                                            const OutputDevice* pOut = this->GetFirstOutputDevice();
                                            const vcl::Window* pWin = pOut ? pOut->GetOwnerWindow() : nullptr;
                                            const vcl::Window* pViewShellWindow = pViewShell->GetEditWindowForActiveOLEObj();
                                            if (pWin && pViewShellWindow && pViewShellWindow->IsAncestorOf(*pWin))
                                            {
                                                // in the following code escaping sequences used inside raw literal strings
                                                // are for making them understandable by the JSON parser

                                                Point aOffsetPx = pWin->GetOffsetPixelFrom(*pViewShellWindow);
                                                Point aLogicOffset = pWin->PixelToLogic(aOffsetPx);
                                                OString sPolygonElem("<polygon points=\\\""_ostr);
                                                for (sal_uInt32 nIndex = 0; nIndex < nPolySize; ++nIndex)
                                                {
                                                    const basegfx::B2DPoint aB2Point = aPolygon.getB2DPoint(nIndex);
                                                    Point aPoint(aB2Point.getX(), aB2Point.getY());
                                                    aPoint.Move(aLogicOffset.getX(), aLogicOffset.getY());
                                                    if (mbNegativeX)
                                                        aPoint.setX(-aPoint.X());
                                                    if (nIndex > 0)
                                                        sPolygonElem += " ";
                                                    sPolygonElem += aPoint.toString();
                                                }
                                                sPolygonElem += R"elem(\" style=\"stroke: none; fill: rgb(114,159,207); fill-opacity: 0.8\"/>)elem";

                                                OString sSVGElem = R"elem(<svg version=\"1.2\" width=\")elem" +
                                                    OString::number(aSelection.GetWidth() / 100.0) +
                                                    R"elem(mm\" height=\")elem" +
                                                    OString::number(aSelection.GetHeight() / 100.0) +
                                                    R"elem(mm\" viewBox=\")elem" +
                                                    aSelection.toString() +
                                                    R"elem(\" preserveAspectRatio=\"xMidYMid\" xmlns=\"http://www.w3.org/2000/svg\">)elem";

                                                aExtraInfo.append(", \"svg\": \""
                                                    + sSVGElem
                                                    + "\\n  "
                                                    + sPolygonElem
                                                    + "\\n</svg>"
                                                    "\""); // svg
                                            }
                                        }
                                    }
                                }
                                aExtraInfo.append("}"); // dragInfo
                            }
                        }
                    }
                }
            }
            else
            {
                SfxObjectShell* pObjectShell = pViewShell ? pViewShell->GetObjectShell() : nullptr;
                if (pObjectShell && pObjectShell->IsSignPDF() && pViewShell && pViewShell->GetSignPDFCertificate().Is())
                {
                    // Expose the info that this is the special signature widget that is OK to
                    // move/resize.
                    aExtraInfo.append(", \"isSignature\": true");
                }
            }
            if (!bTableSelection && !pOtherShell && maHdlList.GetHdlCount())
            {
                boost::property_tree::ptree responseJSON;
                boost::property_tree::ptree others;
                boost::property_tree::ptree anchor;
                boost::property_tree::ptree rectangle;
                boost::property_tree::ptree poly;
                boost::property_tree::ptree custom;
                boost::property_tree::ptree nodes;
                for (size_t i = 0; i < maHdlList.GetHdlCount(); i++)
                {
                    SdrHdl *pHdl = maHdlList.GetHdl(i);
                    boost::property_tree::ptree child;
                    boost::property_tree::ptree point;
                    sal_Int32 kind = static_cast<sal_Int32>(pHdl->GetKind());
                    child.put("id", pHdl->GetObjHdlNum());
                    child.put("kind", kind);
                    child.put("pointer", static_cast<sal_Int32>(pHdl->GetPointer()));
                    Point pHdlPos = pHdl->GetPos();
                    pHdlPos.Move(addLogicOffset.getX(), addLogicOffset.getY());
                    if (convertMapMode)
                    {
                        pHdlPos = o3tl::convert(pHdlPos, o3tl::Length::mm100, o3tl::Length::twip);
                    }
                    point.put("x", pHdlPos.getX());
                    point.put("y", pHdlPos.getY());
                    child.add_child("point", point);
                    const auto node = std::make_pair("", child);
                    boost::property_tree::ptree* selectedNode = nullptr;
                    if (kind >= static_cast<sal_Int32>(SdrHdlKind::UpperLeft) && kind <= static_cast<sal_Int32>(SdrHdlKind::LowerRight))
                    {
                        selectedNode = &rectangle;
                    }
                    else if (kind == static_cast<sal_Int32>(SdrHdlKind::Poly))
                    {
                        selectedNode = &poly;
                    }
                    else if (kind == static_cast<sal_Int32>(SdrHdlKind::CustomShape1))
                    {
                        selectedNode = &custom;
                    }
                    else if (kind == static_cast<sal_Int32>(SdrHdlKind::Anchor) || kind == static_cast<sal_Int32>(SdrHdlKind::Anchor_TR))
                    {
                        if (getSdrModelFromSdrView().IsWriter())
                            selectedNode = &anchor;
                        else
                            // put it to others as we don't render them except in writer
                            selectedNode = &others;
                    }
                    else
                    {
                        selectedNode = &others;
                    }
                    std::string sKind = std::to_string(kind);
                    boost::optional< boost::property_tree::ptree& > kindNode = selectedNode->get_child_optional(sKind.c_str());
                    if (!kindNode)
                    {
                        boost::property_tree::ptree newChild;
                        newChild.push_back(node);
                        selectedNode->add_child(sKind.c_str(), newChild);
                    }
                    else
                        kindNode.get().push_back(node);
                }
                nodes.add_child("rectangle", rectangle);
                nodes.add_child("poly", poly);
                nodes.add_child("custom", custom);
                nodes.add_child("anchor", anchor);
                nodes.add_child("others", others);
                responseJSON.add_child("kinds", nodes);
                std::stringstream aStream;
                boost::property_tree::write_json(aStream, responseJSON, /*pretty=*/ false);
                handleArrayStr = ", \"handles\":"_ostr;
                handleArrayStr = handleArrayStr + aStream.str().c_str();
                if (bConnectorSelection)
                {
                    aStream.str("");
                    boost::property_tree::write_json(aStream, aGluePointsTree, /*pretty=*/ false);
                    handleArrayStr = handleArrayStr + ", \"GluePoints\":";
                    handleArrayStr = handleArrayStr + aStream.str().c_str();
                }
            }

            if (mbNegativeX)
            {
                tools::Rectangle aNegatedRect(aSelection);
                aNegatedRect.SetLeft(-aNegatedRect.Left());
                aNegatedRect.SetRight(-aNegatedRect.Right());
                aNegatedRect.Normalize();
                sSelectionText = aNegatedRect.toString() +
                    ", " + OString::number(nRotAngle.get());
            }
            else
            {
                sSelectionText = aSelection.toString() +
                    ", " + OString::number(nRotAngle.get());
            }

            if (!aExtraInfo.isEmpty())
            {
                sSelectionTextView = sSelectionText + ", " + aExtraInfo + "}";

                if (bMediaObj && pOtherShell == nullptr)
                {
                    // Add the URL only if we have a Media Object and
                    // we are the selecting view.
                    SdrMediaObj* mediaObj = dynamic_cast<SdrMediaObj*>(mpMarkedObj);
                    if (mediaObj)
                        aExtraInfo.append(", \"url\": \"" + mediaObj->getTempURL().toUtf8() + "\"");
                }

                SdrPage *pPage = pPageView ? pPageView->GetPage(): nullptr;

                if (pPage && getSdrModelFromSdrView().IsImpress())
                {
                    // Send all objects' rectangles along with the selected object's information.
                    // Other rectangles can be used for aligning the selected object referencing the others.
                    // Replace curly braces with empty string in order to merge it with the resulting string.
                    OString objectRectangles = SdrObjList::GetObjectRectangles(*pPage).replaceAll("{"_ostr, ""_ostr).replaceAll("}"_ostr, ""_ostr);
                    aExtraInfo.append(", \"ObjectRectangles\": "_ostr + objectRectangles);
                }

                aExtraInfo.append(handleArrayStr
                    + "}");
                sSelectionText += ", " + aExtraInfo;
            }
        }

        if (sSelectionText.isEmpty())
        {
            sSelectionText = "EMPTY"_ostr;
            sSelectionTextView = "EMPTY"_ostr;
            if (!pOtherShell && pViewShell)
                pViewShell->NotifyOtherViews(LOK_CALLBACK_TEXT_VIEW_SELECTION, "selection"_ostr, OString());
        }

        if (bTableSelection)
        {
            boost::property_tree::ptree aTableRectangle;
            aTableRectangle.put("x", aSelection.Left());
            aTableRectangle.put("y", aSelection.Top());
            aTableRectangle.put("width", aSelection.GetWidth());
            aTableRectangle.put("height", aSelection.GetHeight());
            aTableJsonTree.push_back(std::make_pair("rectangle", aTableRectangle));

            std::stringstream aStream;
            boost::property_tree::write_json(aStream, aTableJsonTree);
            if (pViewShell)
                pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TABLE_SELECTED, OString(aStream.str()));
        }
        else if (!getSdrModelFromSdrView().IsWriter() && pViewShell)
        {
            pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TABLE_SELECTED, "{}"_ostr);
        }

        if (pOtherShell)
        {
            // Another shell wants to know about our existing
            // selection.
            if (pViewShell != pOtherShell)
                SfxLokHelper::notifyOtherView(pViewShell, pOtherShell, LOK_CALLBACK_GRAPHIC_VIEW_SELECTION, "selection", sSelectionTextView);
        }
        else if (pViewShell)
        {
            // We have a new selection, so both pViewShell and the
            // other views want to know about it.
            pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_GRAPHIC_SELECTION, sSelectionText);

            SfxLokHelper::notifyOtherViews(pViewShell, LOK_CALLBACK_GRAPHIC_VIEW_SELECTION, "selection", sSelectionTextView);
        }
    }
}

void SdrMarkView::SetMarkHandles(SfxViewShell* pOtherShell)
{
    // remember old focus handle values to search for it again
    const SdrHdl* pSaveOldFocusHdl = maHdlList.GetFocusHdl();
    bool bSaveOldFocus(false);
    sal_uInt32 nSavePolyNum(0), nSavePointNum(0);
    SdrHdlKind eSaveKind(SdrHdlKind::Move);
    SdrObject* pSaveObj = nullptr;

    mpMarkingSubSelectionOverlay.reset();
    mpMarkingSelectionOverlay.reset();

    if(pSaveOldFocusHdl
        && pSaveOldFocusHdl->GetObj()
        && dynamic_cast<const SdrPathObj*>(pSaveOldFocusHdl->GetObj()) != nullptr
        && (pSaveOldFocusHdl->GetKind() == SdrHdlKind::Poly || pSaveOldFocusHdl->GetKind() == SdrHdlKind::BezierWeight))
    {
        bSaveOldFocus = true;
        nSavePolyNum = pSaveOldFocusHdl->GetPolyNum();
        nSavePointNum = pSaveOldFocusHdl->GetPointNum();
        pSaveObj = pSaveOldFocusHdl->GetObj();
        eSaveKind = pSaveOldFocusHdl->GetKind();
    }

    // delete/clear all handles. This will always be done, even with areMarkHandlesHidden()
    maHdlList.Clear();
    maHdlList.SetRotateShear(meDragMode==SdrDragMode::Rotate);
    maHdlList.SetDistortShear(meDragMode==SdrDragMode::Shear);
    mpMarkedObj=nullptr;
    mpMarkedPV=nullptr;

    // are handles enabled at all? Create only then
    if(areMarkHandlesHidden())
        return;

    // There can be multiple mark views, but we're only interested in the one that has a window associated.
    const bool bTiledRendering = comphelper::LibreOfficeKit::isActive() && GetFirstOutputDevice() && GetFirstOutputDevice()->GetOutDevType() == OUTDEV_WINDOW;

    const SdrMarkList& rMarkList = GetMarkedObjectList();
    const size_t nMarkCount=rMarkList.GetMarkCount();
    bool bStdDrag=meDragMode==SdrDragMode::Move;
    bool bSingleTextObjMark=false;
    bool bLimitedRotation(false);

    if (nMarkCount==1)
    {
        mpMarkedObj=rMarkList.GetMark(0)->GetMarkedSdrObj();

        if(nullptr != mpMarkedObj)
        {
            bSingleTextObjMark =
                DynCastSdrTextObj( mpMarkedObj) !=  nullptr &&
                static_cast<SdrTextObj*>(mpMarkedObj)->IsTextFrame();

            // RotGrfFlyFrame: we may have limited rotation
            bLimitedRotation = SdrDragMode::Rotate == meDragMode && mpMarkedObj->HasLimitedRotation();
        }
    }

    bool bFrmHdl=ImpIsFrameHandles();

    if (nMarkCount>0)
    {
        mpMarkedPV=rMarkList.GetMark(0)->GetPageView();

        for (size_t nMarkNum=0; nMarkNum<nMarkCount && (mpMarkedPV!=nullptr || !bFrmHdl); ++nMarkNum)
        {
            const SdrMark* pM=rMarkList.GetMark(nMarkNum);

            if (mpMarkedPV!=pM->GetPageView())
            {
                mpMarkedPV=nullptr;
            }
        }
    }

    tools::Rectangle aRect(GetMarkedObjRect());

    if (mpMarkedObj && mpMarkedObj->GetObjIdentifier() == SdrObjKind::Annotation)
    {
        basegfx::B2DRectangle aB2DRect(aRect.Left(), aRect.Top(), aRect.Right(), aRect.Bottom());
        mpMarkingSelectionOverlay = std::make_unique<MarkingSelectionOverlay>(*this, aB2DRect);

        return;

    }

    SfxViewShell* pViewShell = GetSfxViewShell();

    // check if text edit or ole is active and handles need to be suppressed. This may be the case
    // when a single object is selected
    // Using a strict return statement is okay here; no handles means *no* handles.
    if (mpMarkedObj)
    {
        // formerly #i33755#: If TextEdit is active the EditEngine will directly paint
        // to the window, so suppress Overlay and handles completely; a text frame for
        // the active text edit will be painted by the repaint mechanism in
        // SdrObjEditView::ImpPaintOutlinerView in this case. This needs to be reworked
        // in the future
        // Also formerly #122142#: Pretty much the same for SdrCaptionObj's in calc.
        if(static_cast<SdrView*>(this)->IsTextEdit())
        {
            const SdrTextObj* pSdrTextObj = DynCastSdrTextObj(mpMarkedObj);

            if (pSdrTextObj && pSdrTextObj->IsInEditMode())
            {
                if (!bTiledRendering)
                    return;
            }
        }

        // formerly #i118524#: if inplace activated OLE is selected, suppress handles
        const SdrOle2Obj* pSdrOle2Obj = dynamic_cast< const SdrOle2Obj* >(mpMarkedObj);

        if(pSdrOle2Obj && (pSdrOle2Obj->isInplaceActive() || pSdrOle2Obj->isUiActive()))
        {
            return;
        }

        if (!maSubSelectionList.empty())
        {
            mpMarkingSubSelectionOverlay = std::make_unique<MarkingSubSelectionOverlay>(*this, maSubSelectionList);
        }
    }

    if (bFrmHdl)
    {
        if(!aRect.IsEmpty())
        {
            // otherwise nothing is found
            const size_t nSiz0(maHdlList.GetHdlCount());

            if( bSingleTextObjMark )
            {
                mpMarkedObj->AddToHdlList(maHdlList);
            }
            else
            {
                const bool bWdt0(aRect.Left() == aRect.Right());
                const bool bHgt0(aRect.Top() == aRect.Bottom());

                if (bWdt0 && bHgt0)
                {
                    maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.TopLeft(), SdrHdlKind::UpperLeft));
                }
                else if (!bStdDrag && (bWdt0 || bHgt0))
                {
                    maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.TopLeft(), SdrHdlKind::UpperLeft));
                    maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.BottomRight(), SdrHdlKind::LowerRight));
                }
                else
                {
                    if (!bWdt0 && !bHgt0)
                    {
                        maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.TopLeft(), SdrHdlKind::UpperLeft));
                    }

                    if (!bLimitedRotation && !bHgt0)
                    {
                        maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.TopCenter(), SdrHdlKind::Upper));
                    }

                    if (!bWdt0 && !bHgt0)
                    {
                        maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.TopRight(), SdrHdlKind::UpperRight));
                    }

                    if (!bLimitedRotation && !bWdt0)
                    {
                        maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.LeftCenter(), SdrHdlKind::Left ));
                        maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.RightCenter(), SdrHdlKind::Right));
                    }

                    if (!bWdt0 && !bHgt0)
                    {
                        maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.BottomLeft(), SdrHdlKind::LowerLeft));
                    }

                    if (!bLimitedRotation && !bHgt0)
                    {
                        maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.BottomCenter(), SdrHdlKind::Lower));
                    }

                    if (!bWdt0 && !bHgt0)
                    {
                        maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.BottomRight(), SdrHdlKind::LowerRight));
                    }
                }
            }

            // Diagram selection visualization support
            // Caution: CppunitTest_sd_tiledrendering shows that mpMarkedObj *can* actually be nullptr (!)
            if(nullptr != mpMarkedObj && mpMarkedObj->isDiagram())
            {
                mpMarkedObj->AddToHdlList(maHdlList);
            }

            const size_t nSiz1(maHdlList.GetHdlCount());

            // moved setting the missing parameters at SdrHdl here from the
            // single loop above (bSingleTextObjMark), this was missing all
            // the time. Setting SdrObject is now required to correctly get
            // the View-Dependent evtl. GridOffset adapted
            for (size_t i=nSiz0; i<nSiz1; ++i)
            {
                SdrHdl* pHdl=maHdlList.GetHdl(i);
                pHdl->SetObj(mpMarkedObj);
                pHdl->SetPageView(mpMarkedPV);
                pHdl->SetObjHdlNum(sal_uInt16(i-nSiz0));
            }
        }
    }
    else
    {
        bool bDone(false);

        // moved crop handling to non-frame part and the handle creation to SdrGrafObj
        if(1 == nMarkCount && mpMarkedObj && SdrDragMode::Crop == meDragMode)
        {
            // Default addCropHandles from SdrObject does nothing. When pMarkedObj is SdrGrafObj, previous
            // behaviour occurs (code in svx/source/svdraw/svdograf.cxx). When pMarkedObj is SwVirtFlyDrawObj
            // writer takes the responsibility of adding handles (code in sw/source/core/draw/dflyobj.cxx)
            const size_t nSiz0(maHdlList.GetHdlCount());
            mpMarkedObj->addCropHandles(maHdlList);
            const size_t nSiz1(maHdlList.GetHdlCount());

            // Was missing: Set infos at SdrCropHdl
            for (size_t i=nSiz0; i<nSiz1; ++i)
            {
                SdrHdl* pHdl=maHdlList.GetHdl(i);
                pHdl->SetObj(mpMarkedObj);
                pHdl->SetPageView(mpMarkedPV);
                pHdl->SetObjHdlNum(sal_uInt16(i-nSiz0));
            }

            bDone = true;
        }

        if(!bDone)
        {
            for (size_t nMarkNum=0; nMarkNum<nMarkCount; ++nMarkNum)
            {
                const SdrMark* pM=rMarkList.GetMark(nMarkNum);
                SdrObject* pObj=pM->GetMarkedSdrObj();
                SdrPageView* pPV=pM->GetPageView();
                const size_t nSiz0=maHdlList.GetHdlCount();
                pObj->AddToHdlList(maHdlList);
                const size_t nSiz1=maHdlList.GetHdlCount();
                bool bPoly=pObj->IsPolyObj();
                const SdrUShortCont& rMrkPnts = pM->GetMarkedPoints();
                for (size_t i=nSiz0; i<nSiz1; ++i)
                {
                    SdrHdl* pHdl=maHdlList.GetHdl(i);
                    pHdl->SetObj(pObj);
                    pHdl->SetPageView(pPV);
                    pHdl->SetObjHdlNum(sal_uInt16(i-nSiz0));

                    if (bPoly)
                    {
                        bool bSelected= rMrkPnts.find( sal_uInt16(i-nSiz0) ) != rMrkPnts.end();
                        pHdl->SetSelected(bSelected);
                        if (mbPlusHdlAlways || bSelected)
                        {
                            SdrHdlList plusList(nullptr);
                            pObj->AddToPlusHdlList(plusList, *pHdl);
                            sal_uInt32 nPlusHdlCnt=plusList.GetHdlCount();
                            for (sal_uInt32 nPlusNum=0; nPlusNum<nPlusHdlCnt; nPlusNum++)
                            {
                                SdrHdl* pPlusHdl=plusList.GetHdl(nPlusNum);
                                pPlusHdl->SetObj(pObj);
                                pPlusHdl->SetPageView(pPV);
                                pPlusHdl->SetPlusHdl(true);
                            }
                            plusList.MoveTo(maHdlList);
                        }
                    }
                }
            }
        }
    }

    // GluePoint handles
    for (size_t nMarkNum=0; nMarkNum<nMarkCount; ++nMarkNum)
    {
        const SdrMark* pM=rMarkList.GetMark(nMarkNum);
        SdrObject* pObj=pM->GetMarkedSdrObj();
        const SdrGluePointList* pGPL=pObj->GetGluePointList();
        if (!pGPL)
            continue;

        SdrPageView* pPV=pM->GetPageView();
        const SdrUShortCont& rMrkGlue=pM->GetMarkedGluePoints();
        for (sal_uInt16 nId : rMrkGlue)
        {
            //nNum changed to nNumGP because already used in for loop
            sal_uInt16 nNumGP=pGPL->FindGluePoint(nId);
            if (nNumGP!=SDRGLUEPOINT_NOTFOUND)
            {
                const SdrGluePoint& rGP=(*pGPL)[nNumGP];
                Point aPos(rGP.GetAbsolutePos(*pObj));
                std::unique_ptr<SdrHdl> pGlueHdl(new SdrHdl(aPos,SdrHdlKind::Glue));
                pGlueHdl->SetObj(pObj);
                pGlueHdl->SetPageView(pPV);
                pGlueHdl->SetObjHdlNum(nId);
                maHdlList.AddHdl(std::move(pGlueHdl));
            }
        }
    }

    // rotation point/axis of reflection
    if(!bLimitedRotation)
    {
        AddDragModeHdl(meDragMode);
    }

    // sort handles
    maHdlList.Sort();

    // add custom handles (used by other apps, e.g. AnchorPos)
    AddCustomHdl();

    // moved it here to access all the handles for callback.
    if (bTiledRendering && pViewShell)
    {
        SetMarkHandlesForLOKit(aRect, pOtherShell);
    }

    // try to restore focus handle index from remembered values
    if(!bSaveOldFocus)
        return;

    for(size_t a = 0; a < maHdlList.GetHdlCount(); ++a)
    {
        SdrHdl* pCandidate = maHdlList.GetHdl(a);

        if(pCandidate->GetObj()
            && pCandidate->GetObj() == pSaveObj
            && pCandidate->GetKind() == eSaveKind
            && pCandidate->GetPolyNum() == nSavePolyNum
            && pCandidate->GetPointNum() == nSavePointNum)
        {
            maHdlList.SetFocusHdl(pCandidate);
            break;
        }
    }
}

void SdrMarkView::AddCustomHdl()
{
    // add custom handles (used by other apps, e.g. AnchorPos)
}

void SdrMarkView::SetDragMode(SdrDragMode eMode)
{
    SdrDragMode eMode0=meDragMode;
    meDragMode=eMode;
    if (meDragMode==SdrDragMode::Resize) meDragMode=SdrDragMode::Move;
    if (meDragMode!=eMode0) {
        ForceRefToMarked();
        SetMarkHandles(nullptr);
        {
            const SdrMarkList& rMarkList = GetMarkedObjectList();
            if (rMarkList.GetMarkCount() != 0) MarkListHasChanged();
        }
    }
}

void SdrMarkView::AddDragModeHdl(SdrDragMode eMode)
{
    switch(eMode)
    {
        case SdrDragMode::Rotate:
        {
            // add rotation center
            maHdlList.AddHdl(std::make_unique<SdrHdl>(maRef1, SdrHdlKind::Ref1));
            break;
        }
        case SdrDragMode::Mirror:
        {
            // add axis of reflection
            std::unique_ptr<SdrHdl> pHdl3(new SdrHdl(maRef2, SdrHdlKind::Ref2));
            std::unique_ptr<SdrHdl> pHdl2(new SdrHdl(maRef1, SdrHdlKind::Ref1));
            std::unique_ptr<SdrHdl> pHdl1(new SdrHdlLine(*pHdl2, *pHdl3, SdrHdlKind::MirrorAxis));

            pHdl1->SetObjHdlNum(1); // for sorting
            pHdl2->SetObjHdlNum(2); // for sorting
            pHdl3->SetObjHdlNum(3); // for sorting

            maHdlList.AddHdl(std::move(pHdl1)); // line comes first, so it is the last in HitTest
            maHdlList.AddHdl(std::move(pHdl2));
            maHdlList.AddHdl(std::move(pHdl3));

            break;
        }
        case SdrDragMode::Transparence:
        {
            // add interactive transparency handle
            const SdrMarkList& rMarkList = GetMarkedObjectList();
            const size_t nMarkCount = rMarkList.GetMarkCount();
            if(nMarkCount == 1)
            {
                SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
                SdrModel& rModel = GetModel();
                const SfxItemSet& rSet = pObj->GetMergedItemSet();

                if(SfxItemState::SET != rSet.GetItemState(XATTR_FILLFLOATTRANSPARENCE, false))
                {
                    // add this item, it's not yet there
                    XFillFloatTransparenceItem aNewItem(rSet.Get(XATTR_FILLFLOATTRANSPARENCE));
                    basegfx::BGradient aGrad = aNewItem.GetGradientValue();

                    aNewItem.SetEnabled(true);
                    aGrad.SetStartIntens(100);
                    aGrad.SetEndIntens(100);
                    aNewItem.SetGradientValue(aGrad);

                    // add undo to allow user to take back this step
                    if (rModel.IsUndoEnabled())
--> --------------------

--> maximum size reached

--> --------------------

[ Verzeichnis aufwärts0.68unsichere Verbindung  ]