Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/LibreOffice/svx/source/svdraw/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 111 kB image not shown  

Quelle  svdedxv.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 <com/sun/star/i18n/WordType.hpp>
#include <editeng/editdata.hxx>
#include <editeng/editeng.hxx>
#include <editeng/editobj.hxx>
#include <editeng/editstat.hxx>
#include <editeng/outlobj.hxx>
#include <editeng/unotext.hxx>
#include <o3tl/deleter.hxx>
#include <officecfg/Office/Common.hxx>
#include <svl/itemiter.hxx>
#include <svl/style.hxx>
#include <svl/whiter.hxx>
#include <svx/sdtfchim.hxx>
#include <svx/selectioncontroller.hxx>
#include <svx/svdedxv.hxx>
#include <svx/svdetc.hxx>
#include <svx/svdotable.hxx>
#include <svx/svdotext.hxx>
#include <svx/svdoutl.hxx>
#include <svx/svdpage.hxx>
#include <svx/svdpagv.hxx>
#include <svx/svdundo.hxx>
#include <vcl/canvastools.hxx>
#include <vcl/commandevent.hxx>
#include <vcl/cursor.hxx>
#include <vcl/weld.hxx>
#include <vcl/window.hxx>
#include <comphelper/lok.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <drawinglayer/processor2d/baseprocessor2d.hxx>
#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
#include <drawinglayer/processor2d/processor2dtools.hxx>
#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
#include <editeng/outliner.hxx>
#include <sal/log.hxx>
#include <sdr/overlay/overlaytools.hxx>
#include <sfx2/viewsh.hxx>
#include <svx/dialmgr.hxx>
#include <svx/sdr/overlay/overlaymanager.hxx>
#include <svx/sdr/overlay/overlayselection.hxx>
#include <svx/sdr/table/tablecontroller.hxx>
#include <svx/sdrpagewindow.hxx>
#include <svx/sdrpaintwindow.hxx>
#include <svx/sdrundomanager.hxx>
#include <svx/strings.hrc>
#include <svx/svdviter.hxx>
#include <svtools/optionsdrawinglayer.hxx>
#include <textchain.hxx>
#include <textchaincursor.hxx>
#include <tools/debug.hxx>
#include <vcl/svapp.hxx>
#include <svx/sdr/contact/viewcontact.hxx>

#include <memory>

SdrObjEditView::SdrObjEditView(SdrModel& rSdrModel, OutputDevice* pOut)
    : SdrGlueEditView(rSdrModel, pOut)
    , maTEOverlayGroup()
    , maTextEditUpdateTimer("TextEditUpdateTimer")
    , mxWeakTextEditObj()
    , mpTextEditPV(nullptr)
    , mpTextEditOutlinerView(nullptr)
    , mpTextEditWin(nullptr)
    , m_pTextEditCursorBuffer(nullptr)
    , m_pMacroObj(nullptr)
    , m_pMacroPV(nullptr)
    , m_pMacroWin(nullptr)
    , m_aTextEditArea()
    , m_aMinTextEditArea()
    , m_aOldCalcFieldValueLink()
    , m_aMacroDownPos()
    , m_nMacroTol(0)
    , mbTextEditDontDelete(false)
    , mbTextEditOnlyOneView(false)
    , mbTextEditNewObj(false)
    , mbQuickTextEditMode(true)
    , mbMacroDown(false)
    , mbInteractiveSlideShow(false)
    , mxSelectionController()
    , mxLastSelectionController()
    , mpOldTextEditUndoManager(nullptr)
    , mpLocalTextEditUndoManager()
{
    // init some timer settings (not starting it of course)
    maTextEditUpdateTimer.SetTimeout(EDIT_UPDATEDATA_TIMEOUT);
    maTextEditUpdateTimer.SetInvokeHandler(LINK(this, SdrObjEditView, TextEditUpdate));
}

IMPL_LINK_NOARG(SdrObjEditView, ImpModifyHdl, LinkParamNone*, void)
{
    // IASS: active TextEdit had a model change. Check and react.
    if (nullptr == mpTextEditOutliner)
        // no Outliner, no TextEdit
        return;

    if (!mxWeakTextEditObj.get().is())
        // no TextObject, no TextEdit
        return;

    // reset & restart the timer
    maTextEditUpdateTimer.SetTimeout(EDIT_UPDATEDATA_TIMEOUT);
    maTextEditUpdateTimer.Start();
}

IMPL_LINK_NOARG(SdrObjEditView, TextEditUpdate, Timer*, void)
{
    // IASS: text was changed and EDIT_UPDATEDATA_TIMEOUT has passed
    // since last user input
    maTextEditUpdateTimer.Stop();

    // be safe: still in TextEdit?
    if (nullptr == mpTextEditOutliner)
        // no Outliner, no TextEdit
        return;

    if (!mxWeakTextEditObj.get().is())
        // no TextObject, no TextEdit
        return;

    // launch an ObjectChange: This is the straightforward method
    // to get this broadcasted. We do not risk to set the model
    // unwantedly to changed, we had a text edit going on already.
    // This is needed for SlideShow since it is not (yet) using the
    // standard schema with VC/VOC/OC
    if (isInteractiveSlideShow())
        mxWeakTextEditObj.get()->BroadcastObjectChange();

    // force repaint for objects with changed text in all views
    // that are VC/VOC/OC based (SlideShow is not yet)
    sdr::contact::ViewContact& rVC(mxWeakTextEditObj.get()->GetViewContact());

    if (!rVC.hasMultipleViewObjectContacts())
        // only one VOC -> this is us
        return;

    if (nullptr == mpTextEditPV)
        // should not happen, just invalidate all visualizations
        rVC.ActionChanged();
    else
        // invalidate only visualizations in different views:
        // this is important to not cause evtl. high repaint costs
        // in the EditView -> we avoid this by running the TextEdit
        // on the overlay. NOTE: This is only for better performance,
        // any repaint will just work fine and do the right thing
        rVC.ActionChangedIfDifferentPageView(*mpTextEditPV);
}

SdrObjEditView::~SdrObjEditView()
{
    maTextEditUpdateTimer.Stop();
    mpTextEditWin = nullptr; // so there's no ShowCursor in SdrEndTextEdit
    assert(!IsTextEdit());
    if (IsTextEdit())
        suppress_fun_call_w_exception(SdrEndTextEdit());
    mpTextEditOutliner.reset();
    assert(nullptr == mpOldTextEditUndoManager); // should have been reset
}

bool SdrObjEditView::IsAction() const { return IsMacroObj() || SdrGlueEditView::IsAction(); }

void SdrObjEditView::MovAction(const Point& rPnt)
{
    if (IsMacroObj())
        MovMacroObj(rPnt);
    SdrGlueEditView::MovAction(rPnt);
}

void SdrObjEditView::EndAction()
{
    if (IsMacroObj())
        EndMacroObj();
    SdrGlueEditView::EndAction();
}

void SdrObjEditView::BckAction()
{
    BrkMacroObj();
    SdrGlueEditView::BckAction();
}

void SdrObjEditView::BrkAction()
{
    BrkMacroObj();
    SdrGlueEditView::BrkAction();
}

SdrPageView* SdrObjEditView::ShowSdrPage(SdrPage* pPage)
{
    SdrPageView* pPageView = SdrGlueEditView::ShowSdrPage(pPage);

    if (comphelper::LibreOfficeKit::isActive() && pPageView)
    {
        // Check if other views have an active text edit on the same page as
        // this one.
        SdrViewIter::ForAllViews(pPageView->GetPage(), [this](SdrView* pView) {
            if (pView == this || !pView->IsTextEdit())
                return;

            OutputDevice* pOutDev = GetFirstOutputDevice();
            if (!pOutDev || pOutDev->GetOutDevType() != OUTDEV_WINDOW)
                return;

            // Found one, so create an outliner view, to get invalidations when
            // the text edit changes.
            // Call GetSfxViewShell() to make sure ImpMakeOutlinerView()
            // registers the view shell of this draw view, and not the view
            // shell of pView.
            OutlinerView* pOutlinerView
                = pView->ImpMakeOutlinerView(pOutDev->GetOwnerWindow(), nullptr, GetSfxViewShell());
            pOutlinerView->HideCursor();
            pView->GetTextEditOutliner()->InsertView(pOutlinerView);
        });
    }

    return pPageView;
}

namespace
{
/// Removes outliner views registered in other draw views that use pOutputDevice.
void lcl_RemoveTextEditOutlinerViews(SdrObjEditView const* pThis, SdrPageView const* pPageView,
                                     OutputDevice const* pOutputDevice)
{
    if (!comphelper::LibreOfficeKit::isActive())
        return;

    if (!pPageView)
        return;

    if (!pOutputDevice || pOutputDevice->GetOutDevType() != OUTDEV_WINDOW)
        return;

    SdrViewIter::ForAllViews(pPageView->GetPage(), [&pThis, &pOutputDevice](SdrView* pView) {
        if (pView == pThis || !pView->IsTextEdit())
            return;

        SdrOutliner* pOutliner = pView->GetTextEditOutliner();
        for (size_t nView = 0; nView < pOutliner->GetViewCount(); ++nView)
        {
            OutlinerView* pOutlinerView = pOutliner->GetView(nView);
            if (pOutlinerView->GetWindow()->GetOutDev() != pOutputDevice)
                continue;

            pOutliner->RemoveView(pOutlinerView);
            delete pOutlinerView;
        }
    });
}
}

void SdrObjEditView::HideSdrPage()
{
    lcl_RemoveTextEditOutlinerViews(this, GetSdrPageView(), GetFirstOutputDevice());

    if (mpTextEditPV == GetSdrPageView())
    {
        // HideSdrPage() will clear mpPageView, avoid a dangling pointer.
        mpTextEditPV = nullptr;
    }

    SdrGlueEditView::HideSdrPage();
}

void SdrObjEditView::TakeActionRect(tools::Rectangle& rRect) const
{
    if (IsMacroObj())
    {
        rRect = m_pMacroObj->GetCurrentBoundRect();
    }
    else
    {
        SdrGlueEditView::TakeActionRect(rRect);
    }
}

void SdrObjEditView::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
{
    SdrGlueEditView::Notify(rBC, rHint);
    if (mpTextEditOutliner == nullptr)
        return;

    // change of printer while editing
    if (rHint.GetId() != SfxHintId::ThisIsAnSdrHint)
        return;

    const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint);
    SdrHintKind eKind = pSdrHint->GetKind();
    if (eKind == SdrHintKind::RefDeviceChange)
    {
        mpTextEditOutliner->SetRefDevice(GetModel().GetRefDevice());
    }
    if (eKind == SdrHintKind::DefaultTabChange)
    {
        mpTextEditOutliner->SetDefTab(GetModel().GetDefaultTabulator());
    }
}

void SdrObjEditView::ModelHasChanged()
{
    SdrGlueEditView::ModelHasChanged();
    rtl::Reference<SdrTextObj> pTextObj = mxWeakTextEditObj.get();
    if (pTextObj && !pTextObj->IsInserted())
        SdrEndTextEdit(); // object deleted
    // TextEditObj changed?
    if (!IsTextEdit())
        return;

    if (pTextObj != nullptr)
    {
        size_t nOutlViewCnt = mpTextEditOutliner->GetViewCount();
        bool bAreaChg = false;
        bool bAnchorChg = false;
        bool bColorChg = false;
        bool bContourFrame = pTextObj->IsContourTextFrame();
        EEAnchorMode eNewAnchor(EEAnchorMode::VCenterHCenter);
        tools::Rectangle aOldArea(m_aMinTextEditArea);
        aOldArea.Union(m_aTextEditArea);
        Color aNewColor;
        { // check area
            Size aPaperMin1;
            Size aPaperMax1;
            tools::Rectangle aEditArea1;
            tools::Rectangle aMinArea1;
            pTextObj->TakeTextEditArea(&aPaperMin1, &aPaperMax1, &aEditArea1, &aMinArea1);
            Point aPvOfs(pTextObj->GetTextEditOffset());

            // add possible GridOffset to up-to-now view-independent EditAreas
            basegfx::B2DVector aGridOffset(0.0, 0.0);
            if (getPossibleGridOffsetForSdrObject(aGridOffset, pTextObj.get(), GetSdrPageView()))
            {
                const Point aOffset(basegfx::fround<tools::Long>(aGridOffset.getX()),
                                    basegfx::fround<tools::Long>(aGridOffset.getY()));

                aEditArea1 += aOffset;
                aMinArea1 += aOffset;
            }

            aEditArea1.Move(aPvOfs.X(), aPvOfs.Y());
            aMinArea1.Move(aPvOfs.X(), aPvOfs.Y());
            tools::Rectangle aNewArea(aMinArea1);
            aNewArea.Union(aEditArea1);

            if (aNewArea != aOldArea || aEditArea1 != m_aTextEditArea
                || aMinArea1 != m_aMinTextEditArea
                || mpTextEditOutliner->GetMinAutoPaperSize() != aPaperMin1
                || mpTextEditOutliner->GetMaxAutoPaperSize() != aPaperMax1)
            {
                m_aTextEditArea = aEditArea1;
                m_aMinTextEditArea = aMinArea1;

                const bool bPrevUpdateLayout = mpTextEditOutliner->SetUpdateLayout(false);
                mpTextEditOutliner->SetMinAutoPaperSize(aPaperMin1);
                mpTextEditOutliner->SetMaxAutoPaperSize(aPaperMax1);
                mpTextEditOutliner->SetPaperSize(Size(0, 0)); // re-format Outliner

                if (!bContourFrame)
                {
                    mpTextEditOutliner->ClearPolygon();
                    EEControlBits nStat = mpTextEditOutliner->GetControlWord();
                    nStat |= EEControlBits::AUTOPAGESIZE;
                    mpTextEditOutliner->SetControlWord(nStat);
                }
                else
                {
                    EEControlBits nStat = mpTextEditOutliner->GetControlWord();
                    nStat &= ~EEControlBits::AUTOPAGESIZE;
                    mpTextEditOutliner->SetControlWord(nStat);
                    tools::Rectangle aAnchorRect;
                    pTextObj->TakeTextAnchorRect(aAnchorRect);
                    pTextObj->ImpSetContourPolygon(*mpTextEditOutliner, aAnchorRect, true);
                }
                for (size_t nOV = 0; nOV < nOutlViewCnt; nOV++)
                {
                    OutlinerView* pOLV = mpTextEditOutliner->GetView(nOV);
                    EVControlBits nStat0 = pOLV->GetControlWord();
                    EVControlBits nStat = nStat0;
                    // AutoViewSize only if not ContourFrame.
                    if (!bContourFrame)
                        nStat |= EVControlBits::AUTOSIZE;
                    else
                        nStat &= ~EVControlBits::AUTOSIZE;
                    if (nStat != nStat0)
                        pOLV->SetControlWord(nStat);
                }

                mpTextEditOutliner->SetUpdateLayout(bPrevUpdateLayout);
                bAreaChg = true;
            }
        }
        if (mpTextEditOutlinerView != nullptr)
        { // check fill and anchor
            EEAnchorMode eOldAnchor = mpTextEditOutlinerView->GetAnchorMode();
            eNewAnchor = pTextObj->GetOutlinerViewAnchorMode();
            bAnchorChg = eOldAnchor != eNewAnchor;
            Color aOldColor(mpTextEditOutlinerView->GetBackgroundColor());
            aNewColor = GetTextEditBackgroundColor(*this);
            bColorChg = aOldColor != aNewColor;
        }
        // refresh always when it's a contour frame. That
        // refresh is necessary since it triggers the repaint
        // which makes the Handles visible. Changes at TakeTextRect()
        // seem to have resulted in a case where no refresh is executed.
        // Before that, a refresh must have been always executed
        // (else this error would have happened earlier), thus I
        // even think here a refresh should be done always.
        // Since follow-up problems cannot even be guessed I only
        // add this one more case to the if below.
        // BTW: It's VERY bad style that here, inside ModelHasChanged()
        // the outliner is again massively changed for the text object
        // in text edit mode. Normally, all necessary data should be
        // set at SdrBeginTextEdit(). Some changes and value assigns in
        // SdrBeginTextEdit() are completely useless since they are set here
        // again on ModelHasChanged().
        if (bContourFrame || bAreaChg || bAnchorChg || bColorChg)
        {
            for (size_t nOV = 0; nOV < nOutlViewCnt; nOV++)
            {
                OutlinerView* pOLV = mpTextEditOutliner->GetView(nOV);
                vcl::Window* pWin = pOLV->GetWindow();
                { // invalidate old OutlinerView area
                    tools::Rectangle aTmpRect(aOldArea);
                    sal_uInt16 nPixSiz = pOLV->GetInvalidateMore() + 1;
                    Size aMore(pWin->PixelToLogic(Size(nPixSiz, nPixSiz)));
                    aTmpRect.AdjustLeft(-(aMore.Width()));
                    aTmpRect.AdjustRight(aMore.Width());
                    aTmpRect.AdjustTop(-(aMore.Height()));
                    aTmpRect.AdjustBottom(aMore.Height());
                    InvalidateOneWin(*pWin->GetOutDev(), aTmpRect);
                }
                if (bAnchorChg)
                    pOLV->SetAnchorMode(eNewAnchor);
                if (bColorChg)
                    pOLV->SetBackgroundColor(aNewColor);

                bool bWasCoursorVisible = pOLV->IsCursorVisible();
                vcl::Cursor* pOldCursor = pWin->GetCursor();
                pOLV->SetOutputArea(
                    m_aTextEditArea); // because otherwise, we're not re-anchoring correctly
                ImpInvalidateOutlinerView(*pOLV);
                // Undo SetOutputArea setting and showing the cursor
                if (!bWasCoursorVisible)
                    pOLV->HideCursor();
                pWin->SetCursor(pOldCursor);
            }
            mpTextEditOutlinerView->ShowCursor();
        }
    }
    ImpMakeTextCursorAreaVisible();
}

namespace
{
class TextEditFrameOverlayObject;
class TextEditHighContrastOverlaySelection;

/**
        Helper class to visualize the content of an active EditView as an
        OverlayObject. These objects work with Primitives and are handled
        from the OverlayManager(s) in place as needed.

        It allows complete visualization of the content of the active
        EditView without the need of Invalidates triggered by the EditView
        and thus avoiding potentially expensive repaints by using the
        automatically buffered Overlay mechanism.

        It buffers as much as possible locally and *only* triggers a real
        change (see call to objectChange()) when really needed.
     */

class TextEditOverlayObject : public sdr::overlay::OverlayObject
{
protected:
    /// local access to associated sdr::overlay::OverlaySelection
    std::unique_ptr<sdr::overlay::OverlaySelection> mxOverlayTransparentSelection;
    std::unique_ptr<TextEditHighContrastOverlaySelection> mxOverlayHighContrastSelection;
    std::unique_ptr<TextEditFrameOverlayObject> mxOverlayFrame;

    /// local definition depends on active OutlinerView
    OutlinerView& mrOutlinerView;

    /// geometry definitions with buffering
    basegfx::B2DRange maLastRange;
    basegfx::B2DRange maRange;

    /// text content definitions with buffering
    drawinglayer::primitive2d::Primitive2DContainer maTextPrimitives;
    drawinglayer::primitive2d::Primitive2DContainer maLastTextPrimitives;

    // geometry creation for OverlayObject, can use local *Last* values
    virtual drawinglayer::primitive2d::Primitive2DContainer
    createOverlayObjectPrimitive2DSequence() override;

public:
    TextEditOverlayObject(const Color& rColor, OutlinerView& rOutlinerView);
    virtual ~TextEditOverlayObject() override;

    sdr::overlay::OverlayObject* getOverlaySelection();
    sdr::overlay::OverlayObject* getOverlayFrame();

    const OutlinerView& getOutlinerView() const { return mrOutlinerView; }

    /// override to check conditions for last createOverlayObjectPrimitive2DSequence
    virtual drawinglayer::primitive2d::Primitive2DContainer
    getOverlayObjectPrimitive2DSequence() const override;

    // data write access. In this OverlayObject we only have the
    // callback that triggers detecting if something *has* changed
    void checkDataChange(const basegfx::B2DRange& rMinTextEditArea);
    void checkSelectionChange();

    const basegfx::B2DRange& getRange() const { return maRange; }
    const drawinglayer::primitive2d::Primitive2DContainer& getTextPrimitives() const
    {
        return maTextPrimitives;
    }
};

class TextEditFrameOverlayObject : public sdr::overlay::OverlayObject
{
private:
    const TextEditOverlayObject& mrTextEditOverlayObject;

    // geometry creation for OverlayObject, can use local *Last* values
    virtual drawinglayer::primitive2d::Primitive2DContainer
    createOverlayObjectPrimitive2DSequence() override;

public:
    TextEditFrameOverlayObject(const TextEditOverlayObject& rTextEditOverlayObject);
    using sdr::overlay::OverlayObject::objectChange;
    virtual ~TextEditFrameOverlayObject() override;
};

class TextEditHighContrastOverlaySelection : public sdr::overlay::OverlayObject
{
private:
    const TextEditOverlayObject& mrTextEditOverlayObject;
    std::vector<basegfx::B2DRange> maRanges;

    // geometry creation for OverlayObject, can use local *Last* values
    virtual drawinglayer::primitive2d::Primitive2DContainer
    createOverlayObjectPrimitive2DSequence() override;

public:
    TextEditHighContrastOverlaySelection(const TextEditOverlayObject& rTextEditOverlayObject);
    void setRanges(std::vector<basegfx::B2DRange>&& rNew);
    virtual ~TextEditHighContrastOverlaySelection() override;
};

TextEditHighContrastOverlaySelection::TextEditHighContrastOverlaySelection(
    const TextEditOverlayObject& rTextEditOverlayObject)
    : OverlayObject(rTextEditOverlayObject.getBaseColor())
    , mrTextEditOverlayObject(rTextEditOverlayObject)
{
    allowAntiAliase(rTextEditOverlayObject.allowsAntiAliase());
    // use selection colors in HighContrast mode
    mbHighContrastSelection = true;
}

void TextEditHighContrastOverlaySelection::setRanges(std::vector<basegfx::B2DRange>&& rNew)
{
    if (rNew != maRanges)
    {
        maRanges = std::move(rNew);
        objectChange();
    }
}

drawinglayer::primitive2d::Primitive2DContainer
TextEditHighContrastOverlaySelection::createOverlayObjectPrimitive2DSequence()
{
    drawinglayer::primitive2d::Primitive2DContainer aRetval;

    size_t nCount = maRanges.size();

    if (nCount)
    {
        basegfx::B2DPolyPolygon aClipPolyPolygon;

        basegfx::BColor aRGBColor(getBaseColor().getBColor());

        for (size_t a = 0; a < nCount; ++a)
            aClipPolyPolygon.append(basegfx::utils::createPolygonFromRect(maRanges[a]));

        // This is used in high contrast mode, we will render the selection
        // with the bg forced to the selection Highlight color and the fg color
        // forced to the HighlightText color
        aRetval.append(new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
            basegfx::B2DPolyPolygon(
                basegfx::utils::createPolygonFromRect(aClipPolyPolygon.getB2DRange())),
            aRGBColor));
        aRetval.append(mrTextEditOverlayObject.getTextPrimitives());
        aRetval.append(new drawinglayer::primitive2d::MaskPrimitive2D(std::move(aClipPolyPolygon),
                                                                      std::move(aRetval)));
    }

    return aRetval;
}

TextEditHighContrastOverlaySelection::~TextEditHighContrastOverlaySelection()
{
    if (getOverlayManager())
    {
        getOverlayManager()->remove(*this);
    }
}

sdr::overlay::OverlayObject* TextEditOverlayObject::getOverlaySelection()
{
    if (mxOverlayTransparentSelection)
        return mxOverlayTransparentSelection.get();
    return mxOverlayHighContrastSelection.get();
}

sdr::overlay::OverlayObject* TextEditOverlayObject::getOverlayFrame()
{
    if (!mxOverlayFrame)
        mxOverlayFrame.reset(new TextEditFrameOverlayObject(*this));
    return mxOverlayFrame.get();
}

drawinglayer::primitive2d::Primitive2DContainer
TextEditOverlayObject::createOverlayObjectPrimitive2DSequence()
{
    drawinglayer::primitive2d::Primitive2DContainer aRetval;

    // add buffered TextPrimitives
    aRetval.append(maTextPrimitives);

    return aRetval;
}

drawinglayer::primitive2d::Primitive2DContainer
TextEditFrameOverlayObject::createOverlayObjectPrimitive2DSequence()
{
    drawinglayer::primitive2d::Primitive2DContainer aRetval;

    /// outer frame visualization
    const double fTransparence(SvtOptionsDrawinglayer::GetTransparentSelectionPercent() * 0.01);
    const sal_uInt16 nPixSiz(mrTextEditOverlayObject.getOutlinerView().GetInvalidateMore() - 1);

    aRetval.push_back(new drawinglayer::primitive2d::OverlayRectanglePrimitive(
        mrTextEditOverlayObject.getRange(), getBaseColor().getBColor(), fTransparence,
        std::max(6, nPixSiz - 2), // grow
        0.0, // shrink
        0.0));

    return aRetval;
}

TextEditOverlayObject::TextEditOverlayObject(const Color& rColor, OutlinerView&&nbsp;rOutlinerView)
    : OverlayObject(rColor)
    , mrOutlinerView(rOutlinerView)
{
    // no AA for TextEdit overlay
    allowAntiAliase(false);

    // create local OverlaySelection - this is an integral part of EditText
    // visualization
    if (Application::GetSettings().GetStyleSettings().GetHighContrastMode())
    {
        mxOverlayHighContrastSelection.reset(new TextEditHighContrastOverlaySelection(*this));
    }
    else
    {
        std::vector<basegfx::B2DRange> aEmptySelection{};
        mxOverlayTransparentSelection.reset(new sdr::overlay::OverlaySelection(
            sdr::overlay::OverlayType::Transparent, rColor, std::move(aEmptySelection), true));
    }
}

TextEditOverlayObject::~TextEditOverlayObject()
{
    mxOverlayTransparentSelection.reset();
    mxOverlayHighContrastSelection.reset();

    if (getOverlayManager())
    {
        getOverlayManager()->remove(*this);
    }
}

TextEditFrameOverlayObject::TextEditFrameOverlayObject(
    const TextEditOverlayObject& rTextEditOverlayObject)
    : OverlayObject(rTextEditOverlayObject.getBaseColor())
    , mrTextEditOverlayObject(rTextEditOverlayObject)
{
    allowAntiAliase(rTextEditOverlayObject.allowsAntiAliase());
    // use selection colors in HighContrast mode
    mbHighContrastSelection = true;
}

TextEditFrameOverlayObject::~TextEditFrameOverlayObject()
{
    if (getOverlayManager())
    {
        getOverlayManager()->remove(*this);
    }
}

drawinglayer::primitive2d::Primitive2DContainer
TextEditOverlayObject::getOverlayObjectPrimitive2DSequence() const
{
    if (!getPrimitive2DSequence().empty())
    {
        if (!maRange.equal(maLastRange) || maLastTextPrimitives != maTextPrimitives)
        {
            // conditions of last local decomposition have changed, delete to force new evaluation
            const_cast<TextEditOverlayObject*>(this)->resetPrimitive2DSequence();
        }
    }

    if (getPrimitive2DSequence().empty())
    {
        // remember new buffered values
        const_cast<TextEditOverlayObject*>(this)->maLastRange = maRange;
        const_cast<TextEditOverlayObject*>(this)->maLastTextPrimitives = maTextPrimitives;
    }

    // call base implementation
    return OverlayObject::getOverlayObjectPrimitive2DSequence();
}

void TextEditOverlayObject::checkDataChange(const basegfx::B2DRange& rMinTextEditArea)
{
    bool bObjectChange(false);

    // check current range
    const tools::Rectangle aOutArea(mrOutlinerView.GetOutputArea());
    basegfx::B2DRange aNewRange = vcl::unotools::b2DRectangleFromRectangle(aOutArea);
    aNewRange.expand(rMinTextEditArea);

    if (aNewRange != maRange)
    {
        maRange = aNewRange;
        bObjectChange = true;
    }

    // check if text primitives did change
    SdrOutliner* pSdrOutliner = dynamic_cast<SdrOutliner*>(&getOutlinerView().GetOutliner());

    if (pSdrOutliner)
    {
        // get TextPrimitives directly from active Outliner
        basegfx::B2DHomMatrix aNewTransformA;
        basegfx::B2DHomMatrix aNewTransformB;
        basegfx::B2DRange aClipRange;
        drawinglayer::primitive2d::Primitive2DContainer aNewTextPrimitives;

        // active Outliner is always in unified oriented coordinate system (currently)
        // so just translate to TopLeft of visible Range. Keep in mind that top-left
        // depends on vertical text and top-to-bottom text attributes
        const tools::Rectangle aVisArea(mrOutlinerView.GetVisArea());
        const bool bVerticalWriting(pSdrOutliner->IsVertical());
        const bool bTopToBottom(pSdrOutliner->IsTopToBottom());
        const double fStartInX(bVerticalWriting && bTopToBottom
                                   ? aOutArea.Right() - aVisArea.Left()
                                   : aOutArea.Left() - aVisArea.Left());
        const double fStartInY(bVerticalWriting && !bTopToBottom
                                   ? aOutArea.Bottom() - aVisArea.Top()
                                   : aOutArea.Top() - aVisArea.Top());

        aNewTransformB.translate(fStartInX, fStartInY);

        // get the current TextPrimitives. This is the most expensive part
        // of this mechanism, it *may* be possible to buffer layouted
        // primitives per ParaPortion with/in/dependent on the EditEngine
        // content if needed. For now, get and compare
        SdrTextObj::impDecomposeBlockTextPrimitiveDirect(
            aNewTextPrimitives, *pSdrOutliner, aNewTransformA, aNewTransformB, aClipRange);

        if (aNewTextPrimitives != maTextPrimitives)
        {
            maTextPrimitives = std::move(aNewTextPrimitives);
            bObjectChange = true;
        }
    }

    if (bObjectChange)
    {
        // if there really *was* a change signal the OverlayManager to
        // refresh this object's visualization
        objectChange();

        if (mxOverlayFrame)
            mxOverlayFrame->objectChange();

        // on data change, always do a SelectionChange, too
        // since the selection is an integral part of text visualization
        checkSelectionChange();
    }
}

void TextEditOverlayObject::checkSelectionChange()
{
    if (!(getOverlaySelection() && getOverlayManager()))
        return;

    std::vector<tools::Rectangle> aLogicRects;
    std::vector<basegfx::B2DRange> aLogicRanges;
    const Size aLogicPixel(getOverlayManager()->getOutputDevice().PixelToLogic(Size(1, 1)));

    // get logic selection
    getOutlinerView().GetSelectionRectangles(aLogicRects);

    aLogicRanges.reserve(aLogicRects.size());
    for (const auto& aRect : aLogicRects)
    {
        // convert from logic Rectangles to logic Ranges, do not forget to add
        // one Unit (in this case logical units for one pixel, pre-calculated)
        aLogicRanges.emplace_back(
            aRect.Left() - aLogicPixel.Width(), aRect.Top() - aLogicPixel.Height(),
            aRect.Right() + aLogicPixel.Width(), aRect.Bottom() + aLogicPixel.Height());
    }

    if (mxOverlayTransparentSelection)
        mxOverlayTransparentSelection->setRanges(std::move(aLogicRanges));
    else
        mxOverlayHighContrastSelection->setRanges(std::move(aLogicRanges));
}
// end of anonymous namespace

// TextEdit

// callback from the active EditView, forward to evtl. existing instances of the
// TextEditOverlayObject(s). This will additionally update the selection which
// is an integral part of the text visualization
void SdrObjEditView::EditViewInvalidate(const tools::Rectangle&)
{
    if (!IsTextEdit())
        return;

    // MinTextRange may have changed. Forward it, too
    const basegfx::B2DRange aMinTextRange
        = vcl::unotools::b2DRectangleFromRectangle(m_aMinTextEditArea);

    for (sal_uInt32 a(0); a < maTEOverlayGroup.count(); a++)
    {
        TextEditOverlayObject* pCandidate
            = dynamic_cast<TextEditOverlayObject*>(&maTEOverlayGroup.getOverlayObject(a));

        if (pCandidate)
        {
            pCandidate->checkDataChange(aMinTextRange);
        }
    }
}

// callback from the active EditView, forward to evtl. existing instances of the
// TextEditOverlayObject(s). This cvall *only* updates the selection visualization
// which is e.g. used when only the selection is changed, but not the text
void SdrObjEditView::EditViewSelectionChange()
{
    if (!IsTextEdit())
        return;

    for (sal_uInt32 a(0); a < maTEOverlayGroup.count(); a++)
    {
        TextEditOverlayObject* pCandidate
            = dynamic_cast<TextEditOverlayObject*>(&maTEOverlayGroup.getOverlayObject(a));

        if (pCandidate)
        {
            pCandidate->checkSelectionChange();
        }
    }
}

OutputDevice& SdrObjEditView::EditViewOutputDevice() const { return *mpTextEditWin->GetOutDev(); }

Point SdrObjEditView::EditViewPointerPosPixel() const
{
    return mpTextEditWin->GetPointerPosPixel();
}

css::uno::Reference<css::datatransfer::clipboard::XClipboard> SdrObjEditView::GetClipboard() const
{
    if (!mpTextEditWin)
        return nullptr;
    return mpTextEditWin->GetClipboard();
}

css::uno::Reference<css::datatransfer::dnd::XDropTarget> SdrObjEditView::GetDropTarget()
{
    if (!mpTextEditWin)
        return nullptr;
    return mpTextEditWin->GetDropTarget();
}

void SdrObjEditView::EditViewInputContext(const InputContext& rInputContext)
{
    if (!mpTextEditWin)
        return;
    mpTextEditWin->SetInputContext(rInputContext);
}

void SdrObjEditView::EditViewCursorRect(const tools::Rectangle& rRect, int nExtTextInputWidth)
{
    if (!mpTextEditWin)
        return;
    mpTextEditWin->SetCursorRect(&rRect, nExtTextInputWidth);
}

void SdrObjEditView::TextEditDrawing(SdrPaintWindow& rPaintWindow)
{
    if (!comphelper::LibreOfficeKit::isActive())
    {
        // adapt all TextEditOverlayObject(s), so call EditViewInvalidate()
        // to update accordingly (will update selection, too). Suppress new
        // stuff when LibreOfficeKit is active
        EditViewInvalidate(tools::Rectangle());
    }
    else
    {
        // draw old text edit stuff
        if (IsTextEdit())
        {
            const SdrOutliner* pActiveOutliner = GetTextEditOutliner();

            if (pActiveOutliner)
            {
                const sal_uInt32 nViewCount(pActiveOutliner->GetViewCount());

                if (nViewCount)
                {
                    const vcl::Region& rRedrawRegion = rPaintWindow.GetRedrawRegion();
                    const tools::Rectangle aCheckRect(rRedrawRegion.GetBoundRect());

                    for (sal_uInt32 i(0); i < nViewCount; i++)
                    {
                        OutlinerView* pOLV = pActiveOutliner->GetView(i);

                        // If rPaintWindow knows that the output device is a render
                        // context and is aware of the underlying vcl::Window,
                        // compare against that; that's how double-buffering can
                        // still find the matching OutlinerView.
                        OutputDevice* pOutputDevice = rPaintWindow.GetWindow()
                                                          ? rPaintWindow.GetWindow()->GetOutDev()
                                                          : &rPaintWindow.GetOutputDevice();
                        if (pOLV->GetWindow()->GetOutDev() == pOutputDevice
                            || comphelper::LibreOfficeKit::isActive())
                        {
                            ImpPaintOutlinerView(*pOLV, aCheckRect,
                                                 rPaintWindow.GetTargetOutputDevice());
                            return;
                        }
                    }
                }
            }
        }
    }
}

void SdrObjEditView::ImpPaintOutlinerView(OutlinerView& rOutlView, const tools::Rectangle& rRect,
                                          OutputDevice& rTargetDevice) const
{
    const SdrTextObj* pText = GetTextEditObject();
    bool bTextFrame(pText && pText->IsTextFrame());
    bool bFitToSize(mpTextEditOutliner->GetControlWord() & EEControlBits::STRETCHING);
    bool bModified(mpTextEditOutliner->IsModified());
    tools::Rectangle aBlankRect(rOutlView.GetOutputArea());
    aBlankRect.Union(m_aMinTextEditArea);
    tools::Rectangle aPixRect(rTargetDevice.LogicToPixel(aBlankRect));

    // in the tiled rendering case, the setup is incomplete, and we very
    // easily get an empty rRect on input - that will cause that everything is
    // clipped; happens in case of editing text inside a shape in Calc.
    // FIXME would be better to complete the setup so that we don't get an
    // empty rRect here
    if (!comphelper::LibreOfficeKit::isActive() || !rRect.IsEmpty())
        aBlankRect.Intersection(rRect);

    rOutlView.GetOutliner().SetUpdateLayout(true); // Bugfix #22596#
    rOutlView.Paint(aBlankRect, &rTargetDevice);

    if (!bModified)
    {
        mpTextEditOutliner->ClearModifyFlag();
    }

    if (bTextFrame && !bFitToSize)
    {
        // completely reworked to use primitives; this ensures same look and functionality
        const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
        std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> xProcessor(
            drawinglayer::processor2d::createProcessor2DFromOutputDevice(rTargetDevice,
                                                                         aViewInformation2D));

        const bool bMapModeEnabled(rTargetDevice.IsMapModeEnabled());
        const basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(aPixRect);
        const Color aHilightColor(SvtOptionsDrawinglayer::getHilightColor());
        const double fTransparence(SvtOptionsDrawinglayer::GetTransparentSelectionPercent() * 0.01);
        const sal_uInt16 nPixSiz(rOutlView.GetInvalidateMore() - 1);
        const drawinglayer::primitive2d::Primitive2DReference xReference(
            new drawinglayer::primitive2d::OverlayRectanglePrimitive(
                aRange, aHilightColor.getBColor(), fTransparence, std::max(6, nPixSiz - 2), // grow
                0.0, // shrink
                0.0));
        const drawinglayer::primitive2d::Primitive2DContainer aSequence{ xReference };

        rTargetDevice.EnableMapMode(false);
        xProcessor->process(aSequence);
        rTargetDevice.EnableMapMode(bMapModeEnabled);
    }

    rOutlView.ShowCursor(/*bGotoCursor=*/true, /*bActivate=*/true);
}

void SdrObjEditView::ImpInvalidateOutlinerView(OutlinerView const& rOutlView) const
{
    vcl::Window* pWin = rOutlView.GetWindow();

    if (!pWin)
        return;

    const SdrTextObj* pText = GetTextEditObject();
    bool bTextFrame(pText && pText->IsTextFrame());
    bool bFitToSize(pText && pText->IsFitToSize());

    if (!bTextFrame || bFitToSize)
        return;

    tools::Rectangle aBlankRect(rOutlView.GetOutputArea());
    aBlankRect.Union(m_aMinTextEditArea);
    tools::Rectangle aPixRect(pWin->LogicToPixel(aBlankRect));
    sal_uInt16 nPixSiz(rOutlView.GetInvalidateMore() - 1);

    aPixRect.AdjustLeft(-1);
    aPixRect.AdjustTop(-1);
    aPixRect.AdjustRight(1);
    aPixRect.AdjustBottom(1);

    {
        // limit xPixRect because of driver problems when pixel coordinates are too far out
        Size aMaxXY(pWin->GetOutputSizePixel());
        tools::Long a(2 * nPixSiz);
        tools::Long nMaxX(aMaxXY.Width() + a);
        tools::Long nMaxY(aMaxXY.Height() + a);

        if (aPixRect.Left() < -a)
            aPixRect.SetLeft(-a);
        if (aPixRect.Top() < -a)
            aPixRect.SetTop(-a);
        if (aPixRect.Right() > nMaxX)
            aPixRect.SetRight(nMaxX);
        if (aPixRect.Bottom() > nMaxY)
            aPixRect.SetBottom(nMaxY);
    }

    tools::Rectangle aOuterPix(aPixRect);
    aOuterPix.AdjustLeft(-nPixSiz);
    aOuterPix.AdjustTop(-nPixSiz);
    aOuterPix.AdjustRight(nPixSiz);
    aOuterPix.AdjustBottom(nPixSiz);

    bool bMapModeEnabled(pWin->IsMapModeEnabled());
    pWin->EnableMapMode(false);
    pWin->Invalidate(aOuterPix);
    pWin->EnableMapMode(bMapModeEnabled);
}

OutlinerView* SdrObjEditView::ImpMakeOutlinerView(vcl::Window* pWin, OutlinerView* pGivenView,
                                                  SfxViewShell* pViewShell) const
{
    // background
    Color aBackground(GetTextEditBackgroundColor(*this));
    rtl::Reference<SdrTextObj> pText = mxWeakTextEditObj.get();
    bool bTextFrame = pText != nullptr && pText->IsTextFrame();
    bool bContourFrame = pText != nullptr && pText->IsContourTextFrame();
    // create OutlinerView
    OutlinerView* pOutlView = pGivenView;
    mpTextEditOutliner->SetUpdateLayout(false);

    if (pOutlView == nullptr)
    {
        pOutlView = new OutlinerView(*mpTextEditOutliner, pWin);
    }
    else
    {
        pOutlView->SetWindow(pWin);
    }

    if (mbNegativeX)
        pOutlView->GetEditView().SetNegativeX(mbNegativeX);

    // disallow scrolling
    EVControlBits nStat = pOutlView->GetControlWord();
    nStat &= ~EVControlBits::AUTOSCROLL;
    // AutoViewSize only if not ContourFrame.
    if (!bContourFrame)
        nStat |= EVControlBits::AUTOSIZE;
    if (bTextFrame)
    {
        sal_uInt16 nPixSiz = maHdlList.GetHdlSize() * 2 + 1;
        nStat |= EVControlBits::INVONEMORE;
        pOutlView->SetInvalidateMore(nPixSiz);
    }
    pOutlView->SetControlWord(nStat);
    pOutlView->SetBackgroundColor(aBackground);

    // In case we're in the process of constructing a new view shell,
    // SfxViewShell::Current() may still point to the old one. So if possible,
    // depend on the application owning this draw view to provide the view
    // shell.
    SfxViewShell* pSfxViewShell = pViewShell ? pViewShell : GetSfxViewShell();
    pOutlView->RegisterViewShell(pSfxViewShell ? pSfxViewShell : SfxViewShell::Current());

    if (pText != nullptr)
    {
        pOutlView->SetAnchorMode(pText->GetOutlinerViewAnchorMode());
        mpTextEditOutliner->SetFixedCellHeight(
            pText->GetMergedItem(SDRATTR_TEXT_USEFIXEDCELLHEIGHT).GetValue());
    }
    // do update before setting output area so that aTextEditArea can be recalculated
    mpTextEditOutliner->SetUpdateLayout(true);
    pOutlView->SetOutputArea(m_aTextEditArea);
    ImpInvalidateOutlinerView(*pOutlView);
    return pOutlView;
}

IMPL_LINK(SdrObjEditView, ImpOutlinerStatusEventHdl, EditStatus&, rEditStat, void)
{
    if (mpTextEditOutliner)
    {
        rtl::Reference<SdrTextObj> pTextObj = mxWeakTextEditObj.get();
        if (pTextObj)
        {
            pTextObj->onEditOutlinerStatusEvent(&rEditStat);
        }
    }
}

void SdrObjEditView::ImpChainingEventHdl()
{
    if (!mpTextEditOutliner)
        return;

    rtl::Reference<SdrTextObj> pTextObj = mxWeakTextEditObj.get();
    OutlinerView* pOLV = GetTextEditOutlinerView();
    if (pTextObj && pOLV)
    {
        TextChain* pTextChain = pTextObj->GetTextChain();

        // XXX: IsChainable and GetNilChainingEvent are a bit mixed up atm
        if (!pTextObj->IsChainable())
        {
            return;
        }
        // This is true during an underflow-caused overflow (with pEdtOutl->SetText())
        if (pTextChain->GetNilChainingEvent(pTextObj.get()))
        {
            return;
        }

        // We prevent to trigger further handling of overflow/underflow for pTextObj
        pTextChain->SetNilChainingEvent(pTextObj.get(), true); // XXX

        // Save previous selection pos // NOTE: It must be done to have the right CursorEvent in KeyInput
        pTextChain->SetPreChainingSel(pTextObj.get(), pOLV->GetSelection());
        //maPreChainingSel = new ESelection(pOLV->GetSelection());

        // Handling Undo
        const int nText = 0; // XXX: hardcoded index (SdrTextObj::getText handles only 0)

        const bool bUndoEnabled = IsUndoEnabled();
        std::unique_ptr<SdrUndoObjSetText> pTxtUndo;
        if (bUndoEnabled)
            pTxtUndo.reset(
                dynamic_cast<SdrUndoObjSetText*>(GetModel()
                                                     .GetSdrUndoFactory()
                                                     .CreateUndoObjectSetText(*pTextObj, nText)
                                                     .release()));

        // trigger actual chaining
        pTextObj->onChainingEvent();

        if (pTxtUndo)
        {
            pTxtUndo->AfterSetText();
            if (!pTxtUndo->IsDifferent())
            {
                pTxtUndo.reset();
            }
        }

        if (pTxtUndo)
            AddUndo(std::move(pTxtUndo));

        //maCursorEvent = new CursorChainingEvent(pTextChain->GetCursorEvent(pTextObj));
        //SdrTextObj *pNextLink = pTextObj->GetNextLinkInChain();

        // NOTE: Must be called. Don't let the function return if you set it to true and not reset it
        pTextChain->SetNilChainingEvent(pTextObj.get(), false);
    }
    else
    {
        // XXX
        SAL_INFO("svx.chaining""[OnChaining] No Edit Outliner View");
    }
}

IMPL_LINK_NOARG(SdrObjEditView, ImpAfterCutOrPasteChainingEventHdl, LinkParamNone*void)
{
    SdrTextObj* pTextObj = GetTextEditObject();
    if (!pTextObj)
        return;
    ImpChainingEventHdl();
    TextChainCursorManager aCursorManager(this, pTextObj);
    ImpMoveCursorAfterChainingEvent(&aCursorManager);
}

void SdrObjEditView::ImpMoveCursorAfterChainingEvent(TextChainCursorManager* pCursorManager)
{
    rtl::Reference<SdrTextObj> pTextObj = mxWeakTextEditObj.get();

    if (!pTextObj || !pCursorManager)
        return;

    // Check if it has links to move it to
    if (!pTextObj || !pTextObj->IsChainable())
        return;

    TextChain* pTextChain = pTextObj->GetTextChain();
    ESelection aNewSel = pTextChain->GetPostChainingSel(pTextObj.get());

    pCursorManager->HandleCursorEventAfterChaining(pTextChain->GetCursorEvent(pTextObj.get()),
                                                   aNewSel);

    // Reset event
    pTextChain->SetCursorEvent(pTextObj.get(), CursorChainingEvent::NULL_EVENT);
}

IMPL_LINK(SdrObjEditView, ImpOutlinerCalcFieldValueHdl, EditFieldInfo*, pFI, void)
{
    bool bOk = false;
    OUString& rStr = pFI->GetRepresentation();
    rStr.clear();
    rtl::Reference<SdrTextObj> pTextObj = mxWeakTextEditObj.get();
    if (pTextObj != nullptr)
    {
        std::optional<Color> pTxtCol;
        std::optional<Color> pFldCol;
        std::optional<FontLineStyle> pFldLineStyle;
        bOk = pTextObj->CalcFieldValue(pFI->GetField(), pFI->GetPara(), pFI->GetPos(), true,
                                       pTxtCol, pFldCol, pFldLineStyle, rStr);
        if (bOk)
        {
            if (pTxtCol)
            {
                pFI->SetTextColor(*pTxtCol);
            }
            if (pFldLineStyle)
            {
                pFI->SetFontLineStyle(*pFldLineStyle);
            }
            if (pFldCol)
            {
                pFI->SetFieldColor(*pFldCol);
            }
            else
            {
                pFI->SetFieldColor(COL_LIGHTGRAY); // TODO: remove this later on (357)
            }
        }
    }
    Outliner& rDrawOutl = GetModel().GetDrawOutliner(pTextObj.get());
    Link<EditFieldInfo*, void> aDrawOutlLink = rDrawOutl.GetCalcFieldValueHdl();
    if (!bOk && aDrawOutlLink.IsSet())
    {
        aDrawOutlLink.Call(pFI);
        bOk = !rStr.isEmpty();
    }
    if (!bOk)
    {
        m_aOldCalcFieldValueLink.Call(pFI);
    }
}

IMPL_LINK_NOARG(SdrObjEditView, EndTextEditHdl, SdrUndoManager*, void) { SdrEndTextEdit(); }

// Default implementation - null UndoManager
std::unique_ptr<SdrUndoManager> SdrObjEditView::createLocalTextUndoManager()
{
    SAL_WARN("svx""SdrObjEditView::createLocalTextUndoManager needs to be overridden");
    return std::unique_ptr<SdrUndoManager>();
}

bool SdrObjEditView::SdrBeginTextEdit(SdrObject* pObj_, SdrPageView* pPV, vcl::Window* pWin,
                                      bool bIsNewObj, SdrOutliner* pGivenOutliner,
                                      OutlinerView* pGivenOutlinerView, bool bDontDeleteOutliner,
                                      bool bOnlyOneView, bool bGrabFocus)
{
    // FIXME cannot be an assert() yet, the code is not ready for that;
    // eg. press F7 in Impress when you are inside a text object with spelling
    // mistakes => boom; and it is unclear how to avoid that
    SAL_WARN_IF(IsTextEdit(), "svx""SdrBeginTextEdit called when IsTextEdit() is already true.");
    // FIXME this encourages all sorts of bad habits and should be removed
    SdrEndTextEdit();

    SdrTextObj* pObj = DynCastSdrTextObj(pObj_);
    if (!pObj)
        return false// currently only possible with text objects

    if (bGrabFocus && pWin)
    {
        // attention, this call may cause an EndTextEdit() call to this view
        pWin->GrabFocus(); // to force the cursor into the edit view
    }

    mbTextEditDontDelete = bDontDeleteOutliner && pGivenOutliner != nullptr;
    mbTextEditOnlyOneView = bOnlyOneView;
    mbTextEditNewObj = bIsNewObj;
    const sal_uInt32 nWinCount(PaintWindowCount());

    bool bBrk(false);

    if (!pWin)
    {
        for (sal_uInt32 i = 0; i < nWinCount && !pWin; i++)
        {
            SdrPaintWindow* pPaintWindow = GetPaintWindow(i);

            if (OUTDEV_WINDOW == pPaintWindow->GetOutputDevice().GetOutDevType())
            {
                pWin = pPaintWindow->GetOutputDevice().GetOwnerWindow();
            }
        }

        // break, when no window exists
        if (!pWin)
        {
            bBrk = true;
        }
    }

    if (!bBrk && !pPV)
    {
        pPV = GetSdrPageView();

        // break, when no PageView for the object exists
        if (!pPV)
        {
            bBrk = true;
        }
    }

    // no TextEdit on objects in locked Layer
    if (pPV && pPV->GetLockedLayers().IsSet(pObj->GetLayer()))
    {
        bBrk = true;
    }

    if (mpTextEditOutliner)
    {
        OSL_FAIL("SdrObjEditView::SdrBeginTextEdit(): Old Outliner still exists.");
        mpTextEditOutliner.reset();
    }

    if (!bBrk)
    {
        mpTextEditWin = pWin;
        mpTextEditPV = pPV;
        mxWeakTextEditObj = pObj;
        if (pGivenOutliner)
        {
            mpTextEditOutliner.reset(pGivenOutliner);
            pGivenOutliner = nullptr; // so we don't delete it on the error path
        }
        else
            mpTextEditOutliner
                = SdrMakeOutliner(OutlinerMode::TextObject, pObj->getSdrModelFromSdrObject());

        {
            mpTextEditOutliner->ForceAutoColor(
                officecfg::Office::Common::Accessibility::IsAutomaticFontColor::get());
        }

        m_aOldCalcFieldValueLink = mpTextEditOutliner->GetCalcFieldValueHdl();
        // FieldHdl has to be set by SdrBeginTextEdit, because this call an UpdateFields
        mpTextEditOutliner->SetCalcFieldValueHdl(
            LINK(this, SdrObjEditView, ImpOutlinerCalcFieldValueHdl));
        mpTextEditOutliner->SetBeginPasteOrDropHdl(LINK(this, SdrObjEditView, BeginPasteOrDropHdl));
        mpTextEditOutliner->SetEndPasteOrDropHdl(LINK(this, SdrObjEditView, EndPasteOrDropHdl));

        // It is just necessary to make the visualized page known. Set it.
        mpTextEditOutliner->setVisualizedPage(pPV->GetPage());

        rtl::Reference<SdrTextObj> pTextObj = mxWeakTextEditObj.get();
        mpTextEditOutliner->SetTextObjNoInit(pTextObj.get());

        if (pTextObj->BegTextEdit(*mpTextEditOutliner))
        {
            // switch off any running TextAnimations
            pTextObj->SetTextAnimationAllowed(false);

            // remember old cursor
            if (mpTextEditOutliner->GetViewCount() != 0)
            {
                mpTextEditOutliner->RemoveView(static_cast<size_t>(0));
            }

            // Determine EditArea via TakeTextEditArea.
            // TODO: This could theoretically be left out, because TakeTextRect() calculates the aTextEditArea,
            // but aMinTextEditArea has to happen, too (therefore leaving this in right now)
            pTextObj->TakeTextEditArea(nullptr, nullptr, &m_aTextEditArea, &m_aMinTextEditArea);

            tools::Rectangle aTextRect;
            tools::Rectangle aAnchorRect;
            pTextObj->TakeTextRect(*mpTextEditOutliner, aTextRect, true,
                                   &aAnchorRect /* Give true here, not false */);

            if (!pTextObj->IsContourTextFrame())
            {
                // FitToSize not together with ContourFrame, for now
                if (pTextObj->IsFitToSize())
                    aTextRect = aAnchorRect;
            }

            m_aTextEditArea = aTextRect;

            // add possible GridOffset to up-to-now view-independent EditAreas
            basegfx::B2DVector aGridOffset(0.0, 0.0);
            if (getPossibleGridOffsetForSdrObject(aGridOffset, pTextObj.get(), pPV))
            {
                const Point aOffset(basegfx::fround<tools::Long>(aGridOffset.getX()),
                                    basegfx::fround<tools::Long>(aGridOffset.getY()));

                m_aTextEditArea += aOffset;
                m_aMinTextEditArea += aOffset;
            }

            Point aPvOfs(pTextObj->GetTextEditOffset());
            m_aTextEditArea.Move(aPvOfs.X(), aPvOfs.Y());
            m_aMinTextEditArea.Move(aPvOfs.X(), aPvOfs.Y());
            m_pTextEditCursorBuffer = pWin->GetCursor();

            maHdlList.SetMoveOutside(true);

            // Since IsMarkHdlWhenTextEdit() is ignored, it is necessary
            // to call AdjustMarkHdl() always.
            AdjustMarkHdl();

            mpTextEditOutlinerView = ImpMakeOutlinerView(pWin, pGivenOutlinerView);

            if (!comphelper::LibreOfficeKit::isActive() && mpTextEditOutlinerView)
            {
                // activate visualization of EditView on Overlay, suppress when
                // LibreOfficeKit is active
                mpTextEditOutlinerView->GetEditView().setEditViewCallbacks(this);

                const Color aHilightColor(SvtOptionsDrawinglayer::getHilightColor());
                const SdrTextObj* pText = GetTextEditObject();
                // show for cases like tdf#94223 but not for table cells like tdf#151311
                const bool bVisualizeSurroundingFrame(
                    pText && pText->GetObjIdentifier() != SdrObjKind::Table);
                SdrPageView* pPageView = GetSdrPageView();

                if (pPageView)
                {
                    for (sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
                    {
                        const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);

                        if (rPageWindow.GetPaintWindow().OutputToWindow())
                        {
                            const rtl::Reference<sdr::overlay::OverlayManager>& xManager
                                = rPageWindow.GetOverlayManager();
                            if (xManager.is())
                            {
                                std::unique_ptr<TextEditOverlayObject> pNewTextEditOverlayObject(
                                    new TextEditOverlayObject(aHilightColor,
                                                              *mpTextEditOutlinerView));

                                xManager->add(*pNewTextEditOverlayObject);
                                if (bVisualizeSurroundingFrame)
                                    xManager->add(*pNewTextEditOverlayObject->getOverlayFrame());
                                xManager->add(*pNewTextEditOverlayObject->getOverlaySelection());

                                maTEOverlayGroup.append(std::move(pNewTextEditOverlayObject));
                            }
                        }
                    }
                }
            }

            // check if this view is already inserted
            size_t i2, nCount = mpTextEditOutliner->GetViewCount();
            for (i2 = 0; i2 < nCount; i2++)
            {
                if (mpTextEditOutliner->GetView(i2) == mpTextEditOutlinerView)
                    break;
            }

            if (i2 == nCount)
                mpTextEditOutliner->InsertView(mpTextEditOutlinerView, 0);

            maHdlList.SetMoveOutside(false);
            maHdlList.SetMoveOutside(true);

            // register all windows as OutlinerViews with the Outliner
            if (!bOnlyOneView)
            {
                for (sal_uInt32 i = 0; i < nWinCount; i++)
                {
                    SdrPaintWindow* pPaintWindow = GetPaintWindow(i);
                    OutputDevice& rOutDev = pPaintWindow->GetOutputDevice();

                    if (&rOutDev != pWin->GetOutDev() && OUTDEV_WINDOW == rOutDev.GetOutDevType())
                    {
                        OutlinerView* pOutlView
                            = ImpMakeOutlinerView(rOutDev.GetOwnerWindow(), nullptr);
                        mpTextEditOutliner->InsertView(pOutlView, static_cast<sal_uInt16>(i));
                    }
                }

                if (comphelper::LibreOfficeKit::isActive())
                {
                    // Register an outliner view for all other sdr views that
                    // show the same page, so that when the text edit changes,
                    // all interested windows get an invalidation.
                    SdrViewIter::ForAllViews(pObj->getSdrPageFromSdrObject(), [this, &pWin](
                                                                                  SdrView* pView) {
                        if (pView == this)
                            return;

                        for (sal_uInt32 nViewPaintWindow = 0;
                             nViewPaintWindow < pView->PaintWindowCount(); ++nViewPaintWindow)
                        {
                            SdrPaintWindow* pPaintWindow = pView->GetPaintWindow(nViewPaintWindow);
                            OutputDevice& rOutDev = pPaintWindow->GetOutputDevice();

                            if (&rOutDev != pWin->GetOutDev()
                                && OUTDEV_WINDOW == rOutDev.GetOutDevType())
                            {
                                OutlinerView* pOutlView
                                    = ImpMakeOutlinerView(rOutDev.GetOwnerWindow(), nullptr);
                                pOutlView->HideCursor();
                                rOutDev.GetOwnerWindow()->SetCursor(nullptr);
                                mpTextEditOutliner->InsertView(pOutlView);
                            }
                        }
                    });
                }
            }

            mpTextEditOutlinerView->ShowCursor();
            mpTextEditOutliner->SetStatusEventHdl(
                LINK(this, SdrObjEditView, ImpOutlinerStatusEventHdl));

            // IASS: start listening to ModelChanges of TextEdit
            if (isInteractiveSlideShow()
                || pTextObj->GetViewContact().hasMultipleViewObjectContacts())
                mpTextEditOutliner->SetModifyHdl(LINK(this, SdrObjEditView, ImpModifyHdl));

            if (pTextObj->IsChainable())
            {
                mpTextEditOutlinerView->SetEndCutPasteLinkHdl(
                    LINK(this, SdrObjEditView, ImpAfterCutOrPasteChainingEventHdl));
            }

            mpTextEditOutliner->ClearModifyFlag();

            if (pTextObj->IsFitToSize())
            {
                pWin->Invalidate(m_aTextEditArea);
            }

            SdrHint aHint(SdrHintKind::BeginEdit, *pTextObj);
            GetModel().Broadcast(aHint);
            if (auto pBroadcaster = pTextObj->GetBroadcaster())
                pBroadcaster->Broadcast(aHint);

            mpTextEditOutliner->setVisualizedPage(nullptr);

            if (mxSelectionController.is())
                mxSelectionController->onSelectionHasChanged();

            if (IsUndoEnabled() && !GetModel().GetDisableTextEditUsesCommonUndoManager())
            {
                SdrUndoManager* pSdrUndoManager = nullptr;
                mpLocalTextEditUndoManager = createLocalTextUndoManager();

                if (mpLocalTextEditUndoManager)
                    pSdrUndoManager = mpLocalTextEditUndoManager.get();

                if (pSdrUndoManager)
                {
                    // we have an outliner, undo manager and it's an EditUndoManager, exchange
                    // the document undo manager and the default one from the outliner and tell
                    // it that text edit starts by setting a callback if it needs to end text edit mode.
                    assert(nullptr == mpOldTextEditUndoManager);

                    mpOldTextEditUndoManager = mpTextEditOutliner->SetUndoManager(pSdrUndoManager);
                    pSdrUndoManager->SetEndTextEditHdl(LINK(this, SdrObjEditView, EndTextEditHdl));
                }
                else
                {
                    OSL_ENSURE(false,
                               "The document undo manager is not derived from SdrUndoManager (!)");
                }
            }

            return true// ran fine, let TextEdit run now
        }
        else
        {
            mpTextEditOutliner->SetCalcFieldValueHdl(m_aOldCalcFieldValueLink);
            mpTextEditOutliner->SetBeginPasteOrDropHdl(Link<PasteOrDropInfos*, void>());
            mpTextEditOutliner->SetEndPasteOrDropHdl(Link<PasteOrDropInfos*, void>());
        }
    }
    if (mpTextEditOutliner != nullptr)
    {
        mpTextEditOutliner->setVisualizedPage(nullptr);
    }

    // something went wrong...
    if (!bDontDeleteOutliner)
    {
        delete pGivenOutliner;
        if (pGivenOutlinerView != nullptr)
        {
            delete pGivenOutlinerView;
            pGivenOutlinerView = nullptr;
        }
    }
    mpTextEditOutliner.reset();

    mpTextEditOutlinerView = nullptr;
    mxWeakTextEditObj.clear();
    mpTextEditPV = nullptr;
    mpTextEditWin = nullptr;
    maHdlList.SetMoveOutside(false);

    return false;
}

SdrEndTextEditKind SdrObjEditView::SdrEndTextEdit(bool bDontDeleteReally)
{
    // IASS: stop evtl. running timer immediately
    maTextEditUpdateTimer.Stop();

    SdrEndTextEditKind eRet = SdrEndTextEditKind::Unchanged;
    rtl::Reference<SdrTextObj> pTEObj = mxWeakTextEditObj.get();
    vcl::Window* pTEWin = mpTextEditWin;
    OutlinerView* pTEOutlinerView = mpTextEditOutlinerView;
    vcl::Cursor* pTECursorBuffer = m_pTextEditCursorBuffer;
    SdrUndoManager* pUndoEditUndoManager = nullptr;
    bool bNeedToUndoSavedRedoTextEdit(false);

    if (IsUndoEnabled() && pTEObj && mpTextEditOutliner
        && !GetModel().GetDisableTextEditUsesCommonUndoManager())
    {
        // change back the UndoManager to the remembered original one
        SfxUndoManager* pOriginal = mpTextEditOutliner->SetUndoManager(mpOldTextEditUndoManager);
        mpOldTextEditUndoManager = nullptr;

        if (pOriginal)
        {
            // check if we got back our document undo manager
            SdrUndoManager* pSdrUndoManager = mpLocalTextEditUndoManager.get();

            if (pSdrUndoManager && dynamic_cast<SdrUndoManager*>(pOriginal) == pSdrUndoManager)
            {
                if (pSdrUndoManager->isEndTextEditTriggeredFromUndo())
                {
                    // remember the UndoManager where missing Undos have to be triggered after end
                    // text edit. When the undo had triggered the end text edit, the original action
                    // which had to be undone originally is not yet undone.
                    pUndoEditUndoManager = pSdrUndoManager;

                    // We are ending text edit; if text edit was triggered from undo, execute all redos
                    // to create a complete text change undo action for the redo buffer. Also mark this
                    // state when at least one redo was executed; the created extra TextChange needs to
                    // be undone in addition to the first real undo outside the text edit changes
                    while (pSdrUndoManager->GetRedoActionCount()
                           > pSdrUndoManager->GetRedoActionCountBeforeTextEdit())
                    {
                        bNeedToUndoSavedRedoTextEdit = true;
                        pSdrUndoManager->Redo();
                    }
                }

                // reset the callback link and let the undo manager cleanup all text edit
                // undo actions to get the stack back to the form before the text edit
                pSdrUndoManager->SetEndTextEditHdl(Link<SdrUndoManager*, void>());
            }
            else
            {
                OSL_ENSURE(false"Got UndoManager back in SdrEndTextEdit which is NOT the "
                                  "expected document UndoManager (!)");
                delete pOriginal;
            }

            // cid#1493241 - Wrapper object use after free
            if (pUndoEditUndoManager == mpLocalTextEditUndoManager.get())
                pUndoEditUndoManager = nullptr;
            mpLocalTextEditUndoManager.reset();
        }
    }
    else
    {
        assert(nullptr == mpOldTextEditUndoManager); // cannot be restored!
    }

    if (auto pTextEditObj = mxWeakTextEditObj.get())
    {
        SdrHint aHint(SdrHintKind::EndEdit, *pTextEditObj);
        GetModel().Broadcast(aHint);
        if (auto pBroadcaster = pTextEditObj->GetBroadcaster())
            pBroadcaster->Broadcast(aHint);
    }

    // if new mechanism was used, clean it up. At cleanup no need to check
    // for LibreOfficeKit
    if (mpTextEditOutlinerView)
    {
        mpTextEditOutlinerView->GetEditView().setEditViewCallbacks(nullptr);
        maTEOverlayGroup.clear();
    }

    mxWeakTextEditObj.clear();
    mpTextEditPV = nullptr;
    mpTextEditWin = nullptr;
    mpTextEditOutlinerView = nullptr;
    m_pTextEditCursorBuffer = nullptr;
    m_aTextEditArea = tools::Rectangle();

    if (SdrOutliner* pTEOutliner = mpTextEditOutliner.release())
    {
        bool bModified = pTEOutliner->IsModified();
        if (pTEOutlinerView != nullptr)
--> --------------------

--> maximum size reached

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

Messung V0.5
C=91 H=97 G=93

¤ Dauer der Verarbeitung: 0.12 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.