Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  svdmrkv.cxx   Sprache: C

 
/* -*- 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

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

¤ Dauer der Verarbeitung: 0.20 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge