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


Quelle  impedit.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 "impedit.hxx"
#include <sal/log.hxx>
#include <editeng/editeng.hxx>
#include <editeng/editview.hxx>
#include <editeng/outliner.hxx>
#include <editeng/urlfieldhelper.hxx>
#include <tools/poly.hxx>
#include <editeng/unolingu.hxx>
#include <com/sun/star/linguistic2/XDictionary.hpp>
#include <com/sun/star/datatransfer/dnd/DNDConstants.hpp>
#include <com/sun/star/datatransfer/dnd/XDragGestureRecognizer.hpp>
#include <com/sun/star/datatransfer/dnd/XDropTarget.hpp>
#include <com/sun/star/datatransfer/clipboard/XFlushableClipboard.hpp>
#include <comphelper/lok.hxx>
#include <editeng/flditem.hxx>
#include <svl/intitem.hxx>
#include <vcl/inputctx.hxx>
#include <vcl/transfer.hxx>
#include <vcl/svapp.hxx>
#include <vcl/weldutils.hxx>
#include <vcl/window.hxx>
#include <sot/exchange.hxx>
#include <sot/formats.hxx>
#include <LibreOfficeKit/LibreOfficeKitEnums.h>
#include <comphelper/string.hxx>
#include <sfx2/viewsh.hxx>
#include <sfx2/lokhelper.hxx>
#include <boost/property_tree/ptree.hpp>

using namespace css;

#define SCRLRANGE   20  // Scroll 1/20 of the width/height, when in QueryDrop

static void lcl_AlignToPixel(Point& rPoint, const OutputDevice& rOutDev, short nDiffX, short nDiffY)
{
    rPoint = rOutDev.LogicToPixel( rPoint );

    if ( nDiffX )
        rPoint.AdjustX(nDiffX );
    if ( nDiffY )
        rPoint.AdjustY(nDiffY );

    rPoint = rOutDev.PixelToLogic( rPoint );
}

LOKSpecialPositioning::LOKSpecialPositioning(const ImpEditView& rImpEditView, MapUnit eUnit,
                                             const tools::Rectangle& rOutputArea,
                                             const Point& rVisDocStartPos) :
                                             mrImpEditView(rImpEditView),
                                             maOutArea(rOutputArea),
                                             maVisDocStartPos(rVisDocStartPos),
                                             meUnit(eUnit),
                                             meFlags(LOKSpecialFlags::NONE)
{
}

void LOKSpecialPositioning::ReInit(MapUnit eUnit, const tools::Rectangle& rOutputArea, const Point& rVisDocStartPos)
{
    meUnit = eUnit;
    maOutArea = rOutputArea;
    maVisDocStartPos = rVisDocStartPos;
}

void LOKSpecialPositioning::SetOutputArea(const tools::Rectangle& rOutputArea)
{
    maOutArea = rOutputArea;
}

const tools::Rectangle& LOKSpecialPositioning::GetOutputArea() const
{
    return maOutArea;
}

void LOKSpecialPositioning::SetVisDocStartPos(const Point& rVisDocStartPos)
{
    maVisDocStartPos = rVisDocStartPos;
}

tools::Rectangle LOKSpecialPositioning::GetVisDocArea() const
{
    return tools::Rectangle(GetVisDocLeft(), GetVisDocTop(), GetVisDocRight(), GetVisDocBottom());
}

bool LOKSpecialPositioning::IsVertical() const
{
    return mrImpEditView.IsVertical();
}

bool LOKSpecialPositioning::IsTopToBottom() const
{
    return mrImpEditView.IsTopToBottom();
}

Point LOKSpecialPositioning::GetWindowPos(const Point& rDocPos, MapUnit eDocPosUnit) const
{
    const Point aDocPos = convertUnit(rDocPos, eDocPosUnit);
    Point aPoint;
    if ( !IsVertical() )
    {
        aPoint.setX(aDocPos.X() + maOutArea.Left() - GetVisDocLeft());
        aPoint.setY(aDocPos.Y() + maOutArea.Top() - GetVisDocTop());
    }
    else
    {
        if (IsTopToBottom())
        {
            aPoint.setX(maOutArea.Right() - aDocPos.Y() + GetVisDocTop());
            aPoint.setY(aDocPos.X() + maOutArea.Top() - GetVisDocLeft());
        }
        else
        {
            aPoint.setX(maOutArea.Left() + aDocPos.Y() - GetVisDocTop());
            aPoint.setY(maOutArea.Bottom() - aDocPos.X() + GetVisDocLeft());
        }
    }

    return aPoint;
}

tools::Rectangle LOKSpecialPositioning::GetWindowPos(const tools::Rectangle& rDocRect, MapUnit eDocRectUnit) const
{
    const tools::Rectangle aDocRect = convertUnit(rDocRect, eDocRectUnit);
    Point aPos(GetWindowPos(aDocRect.TopLeft(), meUnit));
    Size aSz = aDocRect.GetSize();
    tools::Rectangle aRect;
    if (!IsVertical())
    {
        aRect = tools::Rectangle(aPos, aSz);
    }
    else
    {
        Point aNewPos(aPos.X() - aSz.Height(), aPos.Y());
        // coverity[swapped_arguments : FALSE] - this is in the correct order
        aRect = tools::Rectangle(aNewPos, Size(aSz.Height(), aSz.Width()));
    }
    return aRect;
}

Point LOKSpecialPositioning::convertUnit(const Point& rPos, MapUnit ePosUnit) const
{
    if (ePosUnit == meUnit)
        return rPos;

    return OutputDevice::LogicToLogic(rPos, MapMode(ePosUnit), MapMode(meUnit));
}

tools::Rectangle LOKSpecialPositioning::convertUnit(const tools::Rectangle& rRect, MapUnit eRectUnit) const
{
    if (eRectUnit == meUnit)
        return rRect;

    return OutputDevice::LogicToLogic(rRect, MapMode(eRectUnit), MapMode(meUnit));
}

Point LOKSpecialPositioning::GetRefPoint() const
{
    return maOutArea.TopLeft();
}

//  class ImpEditView

ImpEditView::ImpEditView(EditView* pView, EditEngine& rEditEngine, vcl::Window* pWindow)
    : mpEditView(pView)
    , mpViewShell(nullptr)
    , mpOtherShell(nullptr)
    , mpEditEngine(&rEditEngine)
    , mpOutputWindow(pWindow)
    , mnInvalidateMore(1)
    , mnControl(EVControlBits::AUTOSCROLL | EVControlBits::ENABLEPASTE)
    , mnTravelXPos(TRAVEL_X_DONTKNOW)
    , mnCursorBidiLevel(CURSOR_BIDILEVEL_DONTKNOW)
    , mnScrollDiffX(0)
    , mbReadOnly(false)
    , mbClickedInSelection(false)
    , mbActiveDragAndDropListener(false)
    , maOutputArea(Point(), rEditEngine.GetPaperSize())
    , meSelectionMode(EESelectionMode::Std)
    , meAnchorMode(EEAnchorMode::TopLeft)
    , mpEditViewCallbacks(nullptr)
    , mbBroadcastLOKViewCursor(comphelper::LibreOfficeKit::isActive())
    , mbSuppressLOKMessages(false)
    , mbNegativeX(false)
{
    maEditSelection.Min() = rEditEngine.GetEditDoc().GetStartPaM();
    maEditSelection.Max() = rEditEngine.GetEditDoc().GetEndPaM();

    SelectionChanged();
}

ImpEditView::~ImpEditView()
{
    RemoveDragAndDropListeners();

    if (mpOutputWindow && (mpOutputWindow->GetCursor() == mpCursor.get()))
        mpOutputWindow->SetCursor( nullptr );
}

void ImpEditView::SetBackgroundColor( const Color& rColor )
{
    mxBackgroundColor = rColor;
}

const Color& ImpEditView::GetBackgroundColor() const
{
    return mxBackgroundColor ? *mxBackgroundColor : GetOutputDevice().GetBackground().GetColor();
}

void ImpEditView::RegisterViewShell(OutlinerViewShell* pViewShell)
{
    mpViewShell = pViewShell;
}

void ImpEditView::RegisterOtherShell(OutlinerViewShell* pOtherShell)
{
    mpOtherShell = pOtherShell;
}

const OutlinerViewShell* ImpEditView::GetViewShell() const
{
    return mpViewShell;
}

void ImpEditView::SetEditSelection( const EditSelection& rEditSelection )
{
    // set state before notification
    maEditSelection = rEditSelection;

    SelectionChanged();

    if (comphelper::LibreOfficeKit::isActive())
    {
        // Tiled rendering: selections are only painted when we are in selection mode.
        getEditEngine().SetInSelectionMode(maEditSelection.HasRange());
    }

    if (getImpEditEngine().GetNotifyHdl().IsSet() )
    {
        const EditDoc& rDoc = getEditEngine().GetEditDoc();
        const EditPaM pmEnd = rDoc.GetEndPaM();
        EENotifyType eNotifyType;
        if (rDoc.Count() > 1 &&
            pmEnd == rEditSelection.Min() &&
            pmEnd == rEditSelection.Max())//if move cursor to the last para.
        {
            eNotifyType = EE_NOTIFY_TEXTVIEWSELECTIONCHANGED_ENDD_PARA;
        }
        else
        {
            eNotifyType = EE_NOTIFY_TEXTVIEWSELECTIONCHANGED;
        }
        EENotify aNotify( eNotifyType );
        getImpEditEngine().GetNotifyHdl().Call( aNotify );
    }

    if (getImpEditEngine().IsFormatted())
    {
        EENotify aNotify(EE_NOTIFY_PROCESSNOTIFICATIONS);
        getImpEditEngine().GetNotifyHdl().Call(aNotify);
    }
}

/// Translate absolute <-> relative twips: LOK wants absolute coordinates as output and gives absolute coordinates as input.
static void lcl_translateTwips(const OutputDevice& rParent, OutputDevice& rChild)
{
    // Don't translate if we already have a non-zero origin.
    // This prevents multiple translate calls that negate
    // one another.
    const Point aOrigin = rChild.GetMapMode().GetOrigin();
    if (aOrigin.getX() != 0 || aOrigin.getY() != 0)
        return;

    // Set map mode, so that callback payloads will contain absolute coordinates instead of relative ones.
    Point aOffset(rChild.GetOutOffXPixel() - rParent.GetOutOffXPixel(), rChild.GetOutOffYPixel() - rParent.GetOutOffYPixel());
    if (!rChild.IsMapModeEnabled())
    {
        MapMode aMapMode(rChild.GetMapMode());
        aMapMode.SetMapUnit(MapUnit::MapTwip);
        aMapMode.SetScaleX(rParent.GetMapMode().GetScaleX());
        aMapMode.SetScaleY(rParent.GetMapMode().GetScaleY());
        rChild.SetMapMode(aMapMode);
        rChild.EnableMapMode();
    }
    aOffset = rChild.PixelToLogic(aOffset);
    MapMode aMapMode(rChild.GetMapMode());
    aMapMode.SetOrigin(aOffset);
    aMapMode.SetMapUnit(rParent.GetMapMode().GetMapUnit());
    rChild.SetMapMode(aMapMode);
    rChild.EnableMapMode(false);
}

// EditView never had a central/secure place to react on SelectionChange since
// Selection was changed in many places, often by not using SetEditSelection()
// but (mis)using GetEditSelection() and manipulating this non-const return
// value. Sorted this out now to have such a place, this is needed for safely
// change/update the Selection visualization for enhanced mechanisms
void ImpEditView::SelectionChanged()
{
    if (EditViewCallbacks* pCallbacks = getEditViewCallbacks())
    {
        // use callback to tell about change in selection visualisation
        pCallbacks->EditViewSelectionChange();
    }
}

// This function is also called when a text's font || size is changed. Because its highlight rectangle must be updated.
void ImpEditView::lokSelectionCallback(const std::optional<tools::PolyPolygon> &pPolyPoly, bool bStartHandleVisible, bool bEndHandleVisible) {
    VclPtr<vcl::Window> pParent = mpOutputWindow->GetParentWithLOKNotifier();
    vcl::Region aRegion( *pPolyPoly );

    if (pParent && pParent->GetLOKWindowId() != 0)
    {
        const tools::Long nX = mpOutputWindow->GetOutOffXPixel() - pParent->GetOutOffXPixel();
        const tools::Long nY = mpOutputWindow->GetOutOffYPixel() - pParent->GetOutOffYPixel();

        std::vector<tools::Rectangle> aRectangles;
        aRegion.GetRegionRectangles(aRectangles);

        std::vector<OString> v;
        for (tools::Rectangle & rRectangle : aRectangles)
        {
            rRectangle = mpOutputWindow->LogicToPixel(rRectangle);
            rRectangle.Move(nX, nY);
            v.emplace_back(rRectangle.toString().getStr());
        }
        OString sRectangle = comphelper::string::join("; ", v);

        const vcl::ILibreOfficeKitNotifier* pNotifier = pParent->GetLOKNotifier();
        std::vector<vcl::LOKPayloadItem> aItems;
        aItems.emplace_back("rectangles", sRectangle);
        aItems.emplace_back("startHandleVisible", OString::boolean(bStartHandleVisible));
        aItems.emplace_back("endHandleVisible", OString::boolean(bEndHandleVisible));
        pNotifier->notifyWindow(pParent->GetLOKWindowId(), u"text_selection"_ustr, aItems);
    }
    else if (mpViewShell)
    {
        mpOutputWindow->GetOutDev()->Push(vcl::PushFlags::MAPMODE);
        if (mpOutputWindow->GetMapMode().GetMapUnit() == MapUnit::MapTwip)
        {
            // Find the parent that is not right
            // on top of us to use its offset.
            vcl::Window* parent = mpOutputWindow->GetParent();
            while (parent &&
                    parent->GetOutOffXPixel() == mpOutputWindow->GetOutOffXPixel() &&
                    parent->GetOutOffYPixel() == mpOutputWindow->GetOutOffYPixel())
            {
                parent = parent->GetParent();
            }

            if (parent)
            {
                lcl_translateTwips(*parent->GetOutDev(), *mpOutputWindow->GetOutDev());
            }
        }

        bool bMm100ToTwip = !mpLOKSpecialPositioning && (mpOutputWindow->GetMapMode().GetMapUnit() == MapUnit::Map100thMM);

        Point aOrigin;
        if (mpOutputWindow->GetMapMode().GetMapUnit() == MapUnit::MapTwip)
            // Writer comments: they use editeng, but are separate widgets.
            aOrigin = mpOutputWindow->GetMapMode().GetOrigin();

        OString sRectangle;
        OString sRefPoint;
        if (mpLOKSpecialPositioning)
            sRefPoint = mpLOKSpecialPositioning->GetRefPoint().toString();

        std::vector<tools::Rectangle> aRectangles;
        aRegion.GetRegionRectangles(aRectangles);

        if (!aRectangles.empty())
        {
            if (mpOutputWindow->IsChart())
            {
                const vcl::Window* pViewShellWindow = mpViewShell->GetEditWindowForActiveOLEObj();
                if (pViewShellWindow && pViewShellWindow->IsAncestorOf(*mpOutputWindow))
                {
                    Point aOffsetPx = mpOutputWindow->GetOffsetPixelFrom(*pViewShellWindow);
                    Point aLogicOffset = mpOutputWindow->PixelToLogic(aOffsetPx);
                    for (tools::Rectangle& rRect : aRectangles)
                        rRect.Move(aLogicOffset.getX(), aLogicOffset.getY());
                }
            }

            std::vector<OString> v;
            for (tools::Rectangle & rRectangle : aRectangles)
            {
                if (bMm100ToTwip)
                {
                    rRectangle = o3tl::convert(rRectangle, o3tl::Length::mm100, o3tl::Length::twip);
                }
                rRectangle.Move(aOrigin.getX(), aOrigin.getY());
                v.emplace_back(rRectangle.toString().getStr());
            }
            sRectangle = comphelper::string::join("; ", v);

            if (mpLOKSpecialPositioning && !sRectangle.isEmpty())
                sRectangle += ":: " + sRefPoint;

            tools::Rectangle& rStart = aRectangles.front();
            tools::Rectangle aStart(rStart.Left(), rStart.Top(), rStart.Left() + 1, rStart.Bottom());

            OString aPayload = aStart.toString();
            if (mpLOKSpecialPositioning)
                aPayload += ":: " + sRefPoint;

            mpViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION_START, aPayload);

            tools::Rectangle& rEnd = aRectangles.back();
            tools::Rectangle aEnd(rEnd.Right() - 1, rEnd.Top(), rEnd.Right(), rEnd.Bottom());

            aPayload = aEnd.toString();
            if (mpLOKSpecialPositioning)
                aPayload += ":: " + sRefPoint;

            mpViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION_END, aPayload);
        }

        if (mpOtherShell)
        {
            // Another shell wants to know about our existing selection.
            if (mpViewShell != mpOtherShell)
                mpViewShell->NotifyOtherView(mpOtherShell, LOK_CALLBACK_TEXT_VIEW_SELECTION, "selection"_ostr, sRectangle);
        }
        else
        {
            mpViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, sRectangle);
            mpViewShell->NotifyOtherViews(LOK_CALLBACK_TEXT_VIEW_SELECTION, "selection"_ostr, sRectangle);
        }

        mpOutputWindow->GetOutDev()->Pop();
    }
}

// renamed from DrawSelection to DrawSelectionXOR to better reflect what this
// method was used for: Paint Selection in XOR, change it and again paint it in XOR.
// This can be safely assumed due to the EditView only being capable of painting the
// selection in XOR until today.
// This also means that all places calling DrawSelectionXOR are thoroughly weighted
// and chosen to make this fragile XOR-paint water-proof and thus contain some
// information in this sense.
// Someone thankfully expanded it to collect the SelectionRectangles when called with
// the Region*, see GetSelectionRectangles below.
void ImpEditView::DrawSelectionXOR( EditSelection aTmpSel, vcl::Region* pRegion, OutputDevice* pTargetDevice )
{
    if (getEditViewCallbacks() && !pRegion && !comphelper::LibreOfficeKit::isActive())
    {
        // we are done, do *not* visualize self
        // CAUTION: do not use when comphelper::LibreOfficeKit::isActive()
        // due to event stuff triggered below. That *should* probably be moved
        // to SelectionChanged() which exists now, but I do not know enough about
        // that stuff to do it
        return;
    }

    if (meSelectionMode == EESelectionMode::Hidden)
        return;

    // It must be ensured before rendering the selection, that the contents of
    // the window is completely valid! Must be here so that in any case if
    // empty, then later on two-Paint Events! Must be done even before the
    // query from bUpdate, if after Invalidate paints still in the queue,
    // but someone switches the update mode!

    // pRegion: When not NULL, then only calculate Region.

    OutputDevice& rTarget = pTargetDevice ? *pTargetDevice : GetOutputDevice();
    bool bClipRegion = rTarget.IsClipRegion();
    vcl::Region aOldRegion = rTarget.GetClipRegion();

    std::optional<tools::PolyPolygon> pPolyPoly;

    if ( !pRegion && !comphelper::LibreOfficeKit::isActive())
    {
        if (!getImpEditEngine().IsUpdateLayout())
            return;
        if (getImpEditEngine().IsInUndo())
            return;

        if ( !aTmpSel.HasRange() )
            return;

        // aTmpOutArea: if OutputArea > Paper width and
        // Text > Paper width ( over large fields )
        tools::Rectangle aTmpOutArea(maOutputArea);
        if ( aTmpOutArea.GetWidth() > getImpEditEngine().GetPaperSize().Width() )
            aTmpOutArea.SetRight( aTmpOutArea.Left() + getImpEditEngine().GetPaperSize().Width() );
        rTarget.IntersectClipRegion( aTmpOutArea );

        if (mpOutputWindow && mpOutputWindow->GetCursor())
            mpOutputWindow->GetCursor()->Hide();
    }

    if (comphelper::LibreOfficeKit::isActive() || pRegion)
        pPolyPoly = tools::PolyPolygon();

    DBG_ASSERT(!getEditEngine().IsIdleFormatterActive(), "DrawSelectionXOR: Not formatted!");
    aTmpSel.Adjust(getEditEngine().GetEditDoc());

    ContentNode* pStartNode = aTmpSel.Min().GetNode();
    ContentNode* pEndNode = aTmpSel.Max().GetNode();
    const sal_Int32 nStartPara = getEditEngine().GetEditDoc().GetPos(pStartNode);
    const sal_Int32 nEndPara = getEditEngine().GetEditDoc().GetPos(pEndNode);
    if (nStartPara == EE_PARA_MAX || nEndPara == EE_PARA_MAX)
        return;

    bool bStartHandleVisible = false;
    bool bEndHandleVisible = false;
    bool bLOKCalcRTL = mpLOKSpecialPositioning &&
        (mpLOKSpecialPositioning->IsLayoutRTL() || getEditEngine().IsRightToLeft(nStartPara));

    auto DrawHighlight = [&, nStartLine = sal_Int32(0), nEndLine = sal_Int32(0)](
                             const ImpEditEngine::LineAreaInfo& rInfo) mutable {
        if (!rInfo.pLine) // Begin of ParaPortion
        {
            if (rInfo.nPortion < nStartPara)
                return ImpEditEngine::CallbackResult::SkipThisPortion;
            if (rInfo.nPortion > nEndPara)
                return ImpEditEngine::CallbackResult::Stop;
            DBG_ASSERT(!rInfo.rPortion.IsInvalid(), "Portion in Selection not formatted!");
            if (rInfo.rPortion.IsInvalid())
                return ImpEditEngine::CallbackResult::SkipThisPortion;

            if (rInfo.nPortion == nStartPara)
                nStartLine = rInfo.rPortion.GetLines().FindLine(aTmpSel.Min().GetIndex(), false);
            else
                nStartLine = 0;

            if (rInfo.nPortion == nEndPara)
                nEndLine = rInfo.rPortion.GetLines().FindLine(aTmpSel.Max().GetIndex(), true);
            else
                nEndLine = rInfo.rPortion.GetLines().Count() - 1;
        }
        else // This is a correct ParaPortion
        {
            if (rInfo.nLine < nStartLine)
                return ImpEditEngine::CallbackResult::Continue;
            if (rInfo.nLine > nEndLine)
                return ImpEditEngine::CallbackResult::SkipThisPortion;

            bool bPartOfLine = false;
            sal_Int32 nStartIndex = rInfo.pLine->GetStart();
            sal_Int32 nEndIndex = rInfo.pLine->GetEnd();
            if ((rInfo.nPortion == nStartPara) && (rInfo.nLine == nStartLine)
                && (nStartIndex != aTmpSel.Min().GetIndex()))
            {
                nStartIndex = aTmpSel.Min().GetIndex();
                bPartOfLine = true;
            }
            if ((rInfo.nPortion == nEndPara) && (rInfo.nLine == nEndLine)
                && (nEndIndex != aTmpSel.Max().GetIndex()))
            {
                nEndIndex = aTmpSel.Max().GetIndex();
                bPartOfLine = true;
            }

            // Can happen if at the beginning of a wrapped line.
            if (nEndIndex < nStartIndex)
                nEndIndex = nStartIndex;

            tools::Rectangle aTmpRect(getImpEditEngine().GetEditCursor(rInfo.rPortion, *rInfo.pLine, nStartIndex, CursorFlags()));
            const Size aLineOffset = getImpEditEngine().getTopLeftDocOffset(rInfo.aArea);
            aTmpRect.Move(0, aLineOffset.Height());

            // Only paint if in the visible range ...
            if (aTmpRect.Top() > GetVisDocBottom())
                return ImpEditEngine::CallbackResult::Continue;

            if (aTmpRect.Bottom() < GetVisDocTop())
                return ImpEditEngine::CallbackResult::Continue;

            if ((rInfo.nPortion == nStartPara) && (rInfo.nLine == nStartLine))
                bStartHandleVisible = true;
            if ((rInfo.nPortion == nEndPara) && (rInfo.nLine == nEndLine))
                bEndHandleVisible = true;

            // Now that we have Bidi, the first/last index doesn't have to be the 'most outside' position
            if (!bPartOfLine)
            {
                Range aLineXPosStartEnd = getEditEngine().GetLineXPosStartEnd(rInfo.rPortion, *rInfo.pLine);
                aTmpRect.SetLeft(aLineXPosStartEnd.Min());
                aTmpRect.SetRight(aLineXPosStartEnd.Max());
                aTmpRect.Move(aLineOffset.Width(), 0);
                ImplDrawHighlightRect(rTarget, aTmpRect.TopLeft(), aTmpRect.BottomRight(),
                                      pPolyPoly ? &*pPolyPoly : nullptr, bLOKCalcRTL);
            }
            else
            {
                sal_Int32 nTmpStartIndex = nStartIndex;
                sal_Int32 nWritingDirStart, nTmpEndIndex;

                while (nTmpStartIndex < nEndIndex)
                {
                    getImpEditEngine().GetRightToLeft(rInfo.nPortion, nTmpStartIndex + 1,
                                                                &nWritingDirStart, &nTmpEndIndex);
                    if (nTmpEndIndex > nEndIndex)
                        nTmpEndIndex = nEndIndex;

                    DBG_ASSERT(nTmpEndIndex > nTmpStartIndex, "DrawSelectionXOR, Start >= End?");

                    tools::Long nX1 = getEditEngine().GetXPos(rInfo.rPortion, *rInfo.pLine, nTmpStartIndex, true);
                    tools::Long nX2 = getEditEngine().GetXPos(rInfo.rPortion, *rInfo.pLine, nTmpEndIndex);

                    aTmpRect.SetLeft(std::min(nX1, nX2));
                    aTmpRect.SetRight(std::max(nX1, nX2));
                    aTmpRect.Move(aLineOffset.Width(), 0);

                    ImplDrawHighlightRect(rTarget, aTmpRect.TopLeft(), aTmpRect.BottomRight(),
                                          pPolyPoly ? &*pPolyPoly : nullptr, bLOKCalcRTL);
                    nTmpStartIndex = nTmpEndIndex;
                }
            }
        }
        return ImpEditEngine::CallbackResult::Continue;
    };
    getImpEditEngine().IterateLineAreas(DrawHighlight, ImpEditEngine::IterFlag::none);

    if (comphelper::LibreOfficeKit::isActive() && mpViewShell && mpOutputWindow)
        lokSelectionCallback(pPolyPoly, bStartHandleVisible, bEndHandleVisible);

    if (pRegion || comphelper::LibreOfficeKit::isActive())
    {
        if (pRegion)
            *pRegion = vcl::Region( *pPolyPoly );
        pPolyPoly.reset();
    }
    else
    {
        if (mpOutputWindow && mpOutputWindow->GetCursor())
            mpOutputWindow->GetCursor()->Show();

        if (bClipRegion)
            rTarget.SetClipRegion(aOldRegion);
        else
            rTarget.SetClipRegion();
    }
}

void ImpEditView::GetSelectionRectangles(EditSelection aTmpSel, std::vector<tools::Rectangle>& rLogicRects)
{
    vcl::Region aRegion;
    DrawSelectionXOR(aTmpSel, &aRegion);
    aRegion.GetRegionRectangles(rLogicRects);
}

void ImpEditView::ImplDrawHighlightRect( OutputDevice& rTarget, const Point& rDocPosTopLeft, const Point& rDocPosBottomRight, tools::PolyPolygon* pPolyPoly, bool bLOKCalcRTL )
{
    if ( rDocPosTopLeft.X() == rDocPosBottomRight.X() )
        return;

    if (mpLOKSpecialPositioning && pPolyPoly)
    {
        MapUnit eDevUnit = rTarget.GetMapMode().GetMapUnit();
        tools::Rectangle aSelRect(rDocPosTopLeft, rDocPosBottomRight);
        aSelRect = GetWindowPos(aSelRect);
        Point aRefPointLogical = GetOutputArea().TopLeft();
        // Get the relative coordinates w.r.t refpoint in display units.
        aSelRect.Move(-aRefPointLogical.X(), -aRefPointLogical.Y());
        if (bLOKCalcRTL)
        {
            tools::Long nMirrorW = GetOutputArea().GetWidth();
            tools::Long nLeft = aSelRect.Left(), nRight = aSelRect.Right();
            aSelRect.SetLeft(nMirrorW - nRight);
            aSelRect.SetRight(nMirrorW - nLeft);
        }
        // Convert from display unit to twips.
        aSelRect = OutputDevice::LogicToLogic(aSelRect, MapMode(eDevUnit), MapMode(MapUnit::MapTwip));

        tools::Polygon aTmpPoly(4);
        aTmpPoly[0] = aSelRect.TopLeft();
        aTmpPoly[1] = aSelRect.TopRight();
        aTmpPoly[2] = aSelRect.BottomRight();
        aTmpPoly[3] = aSelRect.BottomLeft();
        pPolyPoly->Insert(aTmpPoly);
        return;
    }

    bool bPixelMode = rTarget.GetMapMode().GetMapUnit() == MapUnit::MapPixel;

    Point aPnt1( GetWindowPos( rDocPosTopLeft ) );
    Point aPnt2( GetWindowPos( rDocPosBottomRight ) );

    if ( !IsVertical() )
    {
        lcl_AlignToPixel(aPnt1, rTarget, +1, 0);
        lcl_AlignToPixel(aPnt2, rTarget, 0, (bPixelMode ? 0 : -1));
    }
    else
    {
        lcl_AlignToPixel(aPnt1, rTarget, 0, +1 );
        lcl_AlignToPixel(aPnt2, rTarget, (bPixelMode ? 0 : +1), 0);
    }

    tools::Rectangle aRect( aPnt1, aPnt2 );
    if ( pPolyPoly )
    {
        tools::Polygon aTmpPoly( 4 );
        aTmpPoly[0] = aRect.TopLeft();
        aTmpPoly[1] = aRect.TopRight();
        aTmpPoly[2] = aRect.BottomRight();
        aTmpPoly[3] = aRect.BottomLeft();
        pPolyPoly->Insert( aTmpPoly );
    }
    else
    {
        vcl::Window* pWindow = rTarget.GetOwnerWindow();

        if (pWindow)
        {
            pWindow->GetOutDev()->Invert( aRect );
        }
        else
        {
            rTarget.Push(vcl::PushFlags::LINECOLOR|vcl::PushFlags::FILLCOLOR|vcl::PushFlags::RASTEROP);
            rTarget.SetLineColor();
            rTarget.SetFillColor(COL_BLACK);
            rTarget.SetRasterOp(RasterOp::Invert);
            rTarget.DrawRect(aRect);
            rTarget.Pop();
        }
    }
}


bool ImpEditView::IsVertical() const
{
    return getImpEditEngine().IsEffectivelyVertical();
}

bool ImpEditView::IsTopToBottom() const
{
    return getImpEditEngine().IsTopToBottom();
}

tools::Rectangle ImpEditView::GetVisDocArea() const
{
    return tools::Rectangle( GetVisDocLeft(), GetVisDocTop(), GetVisDocRight(), GetVisDocBottom() );
}

Point ImpEditView::GetDocPos( const Point& rWindowPos ) const
{
    // Window Position => Position Document
    Point aPoint;

    if (!getImpEditEngine().IsEffectivelyVertical())
    {
        aPoint.setX( rWindowPos.X() - maOutputArea.Left() + GetVisDocLeft() );
        aPoint.setY( rWindowPos.Y() - maOutputArea.Top() + GetVisDocTop() );
    }
    else
    {
        if (getImpEditEngine().IsTopToBottom())
        {
            aPoint.setX( rWindowPos.Y() - maOutputArea.Top() + GetVisDocLeft() );
            aPoint.setY( maOutputArea.Right() - rWindowPos.X() + GetVisDocTop() );
        }
        else
        {
            aPoint.setX( maOutputArea.Bottom() - rWindowPos.Y() + GetVisDocLeft() );
            aPoint.setY( rWindowPos.X() - maOutputArea.Left() + GetVisDocTop() );
        }
    }

    return aPoint;
}

Point ImpEditView::GetWindowPos( const Point& rDocPos ) const
{
    // Document position => window position
    Point aPoint;

    if (!getImpEditEngine().IsEffectivelyVertical())
    {
        aPoint.setX( rDocPos.X() + maOutputArea.Left() - GetVisDocLeft() );
        aPoint.setY( rDocPos.Y() + maOutputArea.Top() - GetVisDocTop() );
    }
    else
    {
        if (getImpEditEngine().IsTopToBottom())
        {
            aPoint.setX( maOutputArea.Right() - rDocPos.Y() + GetVisDocTop() );
            aPoint.setY( rDocPos.X() + maOutputArea.Top() - GetVisDocLeft() );
        }
        else
        {
            aPoint.setX( maOutputArea.Left() + rDocPos.Y() - GetVisDocTop() );
            aPoint.setY( maOutputArea.Bottom() - rDocPos.X() + GetVisDocLeft() );
        }
    }

    return aPoint;
}

tools::Rectangle ImpEditView::GetWindowPos( const tools::Rectangle& rDocRect ) const
{
    // Document position => window position
    Point aPos( GetWindowPos( rDocRect.TopLeft() ) );
    Size aSz = rDocRect.GetSize();
    tools::Rectangle aRect;
    if (!getImpEditEngine().IsEffectivelyVertical())
    {
        aRect = tools::Rectangle( aPos, aSz );
    }
    else
    {
        Point aNewPos( aPos.X()-aSz.Height(), aPos.Y() );
        // coverity[swapped_arguments : FALSE] - this is in the correct order
        aRect = tools::Rectangle( aNewPos, Size( aSz.Height(), aSz.Width() ) );
    }
    return aRect;
}

void ImpEditView::SetSelectionMode( EESelectionMode eNewMode )
{
    if (meSelectionMode != eNewMode)
    {
        DrawSelectionXOR();
        meSelectionMode = eNewMode;
        DrawSelectionXOR();    // redraw
    }
}

OutputDevice& ImpEditView::GetOutputDevice() const
{
    if (EditViewCallbacks* pCallbacks = getEditViewCallbacks())
        return pCallbacks->EditViewOutputDevice();
    return *mpOutputWindow->GetOutDev();
}

weld::Widget* ImpEditView::GetPopupParent(tools::Rectangle& rRect) const
{
    if (EditViewCallbacks* pCallbacks = getEditViewCallbacks())
    {
        weld::Widget* pParent = pCallbacks->EditViewPopupParent();
        if (pParent)
            return pParent;
    }
    return weld::GetPopupParent(*mpOutputWindow, rRect);
}

void ImpEditView::SetOutputArea( const tools::Rectangle& rRect )
{
    // Here a PixelSnap was used before using GetOutputDevice() and
    // LogicToPixel/PixelToLogic (what was incorrect, would need
    // to take care of 1/2 pixel in logic for rounding). We do not
    // need that anymore, in fact it leads to text slightly
    // 'jumping' around by up to 1 pixel (of course).
    // We paint text nowadays using decomposed TextPrimitives
    // with sub-pixel precision and similar (using a shortcut)
    // for text in TextEdit on the Overlay, also using sub-pixel
    // precision. Just remove this to avoid Text being displayed
    // different in TextEdit and EditView paint visualizations
    // and assign given value to OutputArea unchanged.
    maOutputArea = rRect;

    if (!maOutputArea.IsWidthEmpty() && maOutputArea.Right() < maOutputArea.Left())
        maOutputArea.SetRight(maOutputArea.Left());
    if (!maOutputArea.IsHeightEmpty() && maOutputArea.Bottom() < maOutputArea.Top())
        maOutputArea.SetBottom(maOutputArea.Top());

    SetScrollDiffX( static_cast<sal_uInt16>(maOutputArea.GetWidth()) * 2 / 10 );
}

namespace {

tools::Rectangle lcl_negateRectX(const tools::Rectangle& rRect)
{
    return tools::Rectangle(-rRect.Right(), rRect.Top(), -rRect.Left(), rRect.Bottom());
}

}

void ImpEditView::InvalidateAtWindow(const tools::Rectangle& rRect)
{
    if (EditViewCallbacks* pCallbacks = getEditViewCallbacks())
    {
        // do not invalidate and trigger a global repaint, but forward
        // the need for change to the applied EditViewCallback, can e.g.
        // be used to visualize the active edit text in an OverlayObject
        pCallbacks->EditViewInvalidate(mbNegativeX ? lcl_negateRectX(rRect) : rRect);
    }
    else
    {
        // classic mode: invalidate and trigger full repaint
        // of the changed area
        GetWindow()->Invalidate(mbNegativeX ? lcl_negateRectX(rRect) : rRect);
    }
}

void ImpEditView::ResetOutputArea( const tools::Rectangle& rRect )
{
    // remember old out area
    const tools::Rectangle aOldArea(maOutputArea);

    // apply new one
    SetOutputArea(rRect);

    // invalidate surrounding areas if update is true
    if(aOldArea.IsEmpty() || !getImpEditEngine().IsUpdateLayout())
        return;

    // #i119885# use grown area if needed; do when getting bigger OR smaller
    const sal_Int32 nMore(DoInvalidateMore() ? GetOutputDevice().PixelToLogic(Size(mnInvalidateMore, 0)).Width() : 0);

    if (aOldArea.Left() > maOutputArea.Left())
    {
        const tools::Rectangle aRect(maOutputArea.Left() - nMore, aOldArea.Top() - nMore, aOldArea.Left(), aOldArea.Bottom() + nMore);
        InvalidateAtWindow(aRect);
    }
    else if (aOldArea.Left() < maOutputArea.Left())
    {
        const tools::Rectangle aRect(aOldArea.Left() - nMore, aOldArea.Top() - nMore, maOutputArea.Left(), aOldArea.Bottom() + nMore);
        InvalidateAtWindow(aRect);
    }

    if (aOldArea.Right() > maOutputArea.Right())
    {
        const tools::Rectangle aRect(maOutputArea.Right(), aOldArea.Top() - nMore, aOldArea.Right() + nMore, aOldArea.Bottom() + nMore);
        InvalidateAtWindow(aRect);
    }
    else if (aOldArea.Right() < maOutputArea.Right())
    {
        const tools::Rectangle aRect(aOldArea.Right(), aOldArea.Top() - nMore, maOutputArea.Right() + nMore, aOldArea.Bottom() + nMore);
        InvalidateAtWindow(aRect);
    }

    if (aOldArea.Top() > maOutputArea.Top())
    {
        const tools::Rectangle aRect(aOldArea.Left() - nMore, maOutputArea.Top() - nMore, aOldArea.Right() + nMore, aOldArea.Top());
        InvalidateAtWindow(aRect);
    }
    else if (aOldArea.Top() < maOutputArea.Top())
    {
        const tools::Rectangle aRect(aOldArea.Left() - nMore, aOldArea.Top() - nMore, aOldArea.Right() + nMore, maOutputArea.Top());
        InvalidateAtWindow(aRect);
    }

    if (aOldArea.Bottom() > maOutputArea.Bottom())
    {
        const tools::Rectangle aRect(aOldArea.Left() - nMore, maOutputArea.Bottom(), aOldArea.Right() + nMore, aOldArea.Bottom() + nMore);
        InvalidateAtWindow(aRect);
    }
    else if (aOldArea.Bottom() < maOutputArea.Bottom())
    {
        const tools::Rectangle aRect(aOldArea.Left() - nMore, aOldArea.Bottom(), aOldArea.Right() + nMore, maOutputArea.Bottom() + nMore);
        InvalidateAtWindow(aRect);
    }
}

void ImpEditView::RecalcOutputArea()
{
    Point aNewTopLeft(maOutputArea.TopLeft());
    Size aNewSz(maOutputArea.GetSize());

    // X:
    if ( DoAutoWidth() )
    {
        if (getImpEditEngine().GetStatus().AutoPageWidth())
            aNewSz.setWidth(getImpEditEngine().GetPaperSize().Width());
        switch (meAnchorMode)
        {
            case EEAnchorMode::TopLeft:
            case EEAnchorMode::VCenterLeft:
            case EEAnchorMode::BottomLeft:
            {
                aNewTopLeft.setX(maAnchorPoint.X());
            }
            break;
            case EEAnchorMode::TopHCenter:
            case EEAnchorMode::VCenterHCenter:
            case EEAnchorMode::BottomHCenter:
            {
                aNewTopLeft.setX(maAnchorPoint.X() - aNewSz.Width() / 2);
            }
            break;
            case EEAnchorMode::TopRight:
            case EEAnchorMode::VCenterRight:
            case EEAnchorMode::BottomRight:
            {
                aNewTopLeft.setX(maAnchorPoint.X() - aNewSz.Width() - 1);
            }
            break;
        }
    }

    // Y:
    if ( DoAutoHeight() )
    {
        if (getImpEditEngine().GetStatus().AutoPageHeight())
            aNewSz.setHeight(getImpEditEngine().GetPaperSize().Height());
        switch (meAnchorMode)
        {
            case EEAnchorMode::TopLeft:
            case EEAnchorMode::TopHCenter:
            case EEAnchorMode::TopRight:
            {
                aNewTopLeft.setY(maAnchorPoint.Y());
            }
            break;
            case EEAnchorMode::VCenterLeft:
            case EEAnchorMode::VCenterHCenter:
            case EEAnchorMode::VCenterRight:
            {
                aNewTopLeft.setY(maAnchorPoint.Y() - aNewSz.Height() / 2);
            }
            break;
            case EEAnchorMode::BottomLeft:
            case EEAnchorMode::BottomHCenter:
            case EEAnchorMode::BottomRight:
            {
                aNewTopLeft.setY(maAnchorPoint.Y() - aNewSz.Height() - 1);
            }
            break;
        }
    }
    ResetOutputArea( tools::Rectangle( aNewTopLeft, aNewSz ) );
}

void ImpEditView::SetAnchorMode(EEAnchorMode eMode)
{
    meAnchorMode = eMode;
    CalcAnchorPoint();
}

void ImpEditView::CalcAnchorPoint()
{
    // GetHeight() and GetWidth() -1, because rectangle calculation not preferred.

    // X:
    switch (meAnchorMode)
    {
        case EEAnchorMode::TopLeft:
        case EEAnchorMode::VCenterLeft:
        case EEAnchorMode::BottomLeft:
        {
            maAnchorPoint.setX(maOutputArea.Left());
        }
        break;
        case EEAnchorMode::TopHCenter:
        case EEAnchorMode::VCenterHCenter:
        case EEAnchorMode::BottomHCenter:
        {
            maAnchorPoint.setX(maOutputArea.Left() + (maOutputArea.GetWidth() - 1) / 2);
        }
        break;
        case EEAnchorMode::TopRight:
        case EEAnchorMode::VCenterRight:
        case EEAnchorMode::BottomRight:
        {
            maAnchorPoint.setX(maOutputArea.Right());
        }
        break;
    }

    // Y:
    switch (meAnchorMode)
    {
        case EEAnchorMode::TopLeft:
        case EEAnchorMode::TopHCenter:
        case EEAnchorMode::TopRight:
        {
            maAnchorPoint.setY(maOutputArea.Top());
        }
        break;
        case EEAnchorMode::VCenterLeft:
        case EEAnchorMode::VCenterHCenter:
        case EEAnchorMode::VCenterRight:
        {
            maAnchorPoint.setY(maOutputArea.Top() + (maOutputArea.GetHeight() - 1) / 2);
        }
        break;
        case EEAnchorMode::BottomLeft:
        case EEAnchorMode::BottomHCenter:
        case EEAnchorMode::BottomRight:
        {
            maAnchorPoint.setY(maOutputArea.Bottom() - 1);
        }
        break;
    }
}

namespace
{

// For building JSON message to be sent to Online
boost::property_tree::ptree getHyperlinkPropTree(const OUString& sText, const OUString& sLink)
{
    boost::property_tree::ptree aTree;
    aTree.put("text", sText);
    aTree.put("link", sLink);
    return aTree;
}

// End of anon namespace

tools::Rectangle ImpEditView::ImplGetEditCursor(EditPaM const& aPaM,
    CursorFlags const aShowCursorFlags, sal_Int32& nTextPortionStart,
    ParaPortion const& rParaPortion) const
{
    tools::Rectangle aEditCursor = getImpEditEngine().PaMtoEditCursor(aPaM, aShowCursorFlags);
    if (!IsInsertMode() && !maEditSelection.HasRange())
    {
        if ( aPaM.GetNode()->Len() && ( aPaM.GetIndex() < aPaM.GetNode()->Len() ) )
        {
            // If we are behind a portion, and the next portion has other direction, we must change position...
            aEditCursor.SetLeft(getImpEditEngine().PaMtoEditCursor(aPaM, CursorFlags{.bTextOnly = true, .bPreferPortionStart = true}).Left());
            aEditCursor.SetRight( aEditCursor.Left() );

            sal_Int32 nTextPortion = rParaPortion.GetTextPortions().FindPortion( aPaM.GetIndex(), nTextPortionStart, true );
            const TextPortion& rTextPortion = rParaPortion.GetTextPortions()[nTextPortion];
            if ( rTextPortion.GetKind() == PortionKind::TAB )
            {
                aEditCursor.AdjustRight(rTextPortion.GetSize().Width() );
            }
            else
            {
                EditPaM aNext = getEditEngine().CursorRight( aPaM );
                tools::Rectangle aTmpRect = getImpEditEngine().PaMtoEditCursor(aNext, CursorFlags{ .bTextOnly = true });
                if ( aTmpRect.Top() != aEditCursor.Top() )
                    aTmpRect = getImpEditEngine().PaMtoEditCursor(aNext, CursorFlags{ .bTextOnly = true, .bEndOfLine = true });
                aEditCursor.SetRight( aTmpRect.Left() );
            }
        }
    }

    tools::Long nMaxHeight = !IsVertical() ? maOutputArea.GetHeight() : maOutputArea.GetWidth();
    if ( aEditCursor.GetHeight() > nMaxHeight )
    {
        aEditCursor.SetBottom( aEditCursor.Top() + nMaxHeight - 1 );
    }

    return aEditCursor;
}

tools::Rectangle ImpEditView::GetEditCursor() const
{
    EditPaM aPaM(maEditSelection.Max());

    sal_Int32 nTextPortionStart = 0;
    sal_Int32 nPara = getEditEngine().GetEditDoc().GetPos( aPaM.GetNode() );
    if (nPara == EE_PARA_MAX) // #i94322
        return tools::Rectangle();

    ParaPortion const& rParaPortion = getEditEngine().GetParaPortions().getRef(nPara);

    CursorFlags aShowCursorFlags = maExtraCursorFlags;
    aShowCursorFlags.bTextOnly = true;

    // Use CursorBidiLevel 0/1 in meaning of
    // 0: prefer portion end, normal mode
    // 1: prefer portion start

    if ( ( GetCursorBidiLevel() != CURSOR_BIDILEVEL_DONTKNOW ) && GetCursorBidiLevel() )
    {
        aShowCursorFlags.bPreferPortionStart = true;
    }

    return ImplGetEditCursor(aPaM, aShowCursorFlags, nTextPortionStart, rParaPortion);
}

::std::optional<::std::tuple<tools::Rectangle, tools::Rectangle, CursorFlags, sal_Int32>>
ImpEditView::ImplGetCursorRectAndMaybeScroll(EditPaM const& rPos,
    ParaPortion const& rParaPortion, bool const bGotoCursor)
{
    sal_Int32 nTextPortionStart = 0;
    CursorFlags aShowCursorFlags = maExtraCursorFlags;
    aShowCursorFlags.bTextOnly = true;

    // Use CursorBidiLevel 0/1 in meaning of
    // 0: prefer portion end, normal mode
    // 1: prefer portion start

    if ( ( GetCursorBidiLevel() != CURSOR_BIDILEVEL_DONTKNOW ) && GetCursorBidiLevel() )
    {
        aShowCursorFlags.bPreferPortionStart = true;
    }

    tools::Rectangle aEditCursor = ImplGetEditCursor(rPos, aShowCursorFlags, nTextPortionStart, rParaPortion);

    if ( bGotoCursor  ) // && (!getImpEditEngine().GetStatus().AutoPageSize() ) )
    {
        // check if scrolling is necessary...
        // if scrolling, then update () and Scroll ()!
        tools::Long nDocDiffX = 0;
        tools::Long nDocDiffY = 0;

        tools::Rectangle aTmpVisArea( GetVisDocArea() );
        // aTmpOutArea: if OutputArea > Paper width and
        // Text > Paper width ( over large fields )
        tools::Long nMaxTextWidth = !IsVertical() ? getImpEditEngine().GetPaperSize().Width() : getImpEditEngine().GetPaperSize().Height();
        if ( aTmpVisArea.GetWidth() > nMaxTextWidth )
            aTmpVisArea.SetRight( aTmpVisArea.Left() + nMaxTextWidth );

        if ( aEditCursor.Bottom() > aTmpVisArea.Bottom() )
        {   // Scroll up, here positive
            nDocDiffY = aEditCursor.Bottom() - aTmpVisArea.Bottom();
        }
        else if ( aEditCursor.Top() < aTmpVisArea.Top() )
        {   // Scroll down, here negative
            nDocDiffY = aEditCursor.Top() - aTmpVisArea.Top();
        }

        if ( aEditCursor.Right() > aTmpVisArea.Right() )
        {
            // Scroll left, positive
            nDocDiffX = aEditCursor.Right() - aTmpVisArea.Right();
            // Can it be a little more?
            if ( aEditCursor.Right() < ( nMaxTextWidth - GetScrollDiffX() ) )
                nDocDiffX += GetScrollDiffX();
            else
            {
                tools::Long n = nMaxTextWidth - aEditCursor.Right();
                // If MapMode != RefMapMode then the EditCursor can go beyond
                // the paper width!
                nDocDiffX += ( n > 0 ? n : -n );
            }
        }
        else if ( aEditCursor.Left() < aTmpVisArea.Left() )
        {
            // Scroll right, negative:
            nDocDiffX = aEditCursor.Left() - aTmpVisArea.Left();
            // Can it be a little more?
            if ( aEditCursor.Left() > ( - static_cast<tools::Long>(GetScrollDiffX()) ) )
                nDocDiffX -= GetScrollDiffX();
            else
                nDocDiffX -= aEditCursor.Left();
        }
        if (rPos.GetIndex() == 0) // needed for the Outliner
        {
            // But make sure that the cursor is not leaving visible area
            // because of this!
            if ( aEditCursor.Left() < aTmpVisArea.GetWidth() )
            {
                nDocDiffX = -aTmpVisArea.Left();
            }
        }

        if ( nDocDiffX | nDocDiffY )
        {
            tools::Long nDiffX = !IsVertical() ? nDocDiffX : (IsTopToBottom() ? -nDocDiffY : nDocDiffY);
            tools::Long nDiffY = !IsVertical() ? nDocDiffY : (IsTopToBottom() ? nDocDiffX : -nDocDiffX);

            if ( nDiffX )
                getEditEngine().GetInternalEditStatus().GetStatusWord() = getEditEngine().GetInternalEditStatus().GetStatusWord() | EditStatusFlags::HSCROLL;
            if ( nDiffY )
                getEditEngine().GetInternalEditStatus().GetStatusWord() = getEditEngine().GetInternalEditStatus().GetStatusWord() | EditStatusFlags::VSCROLL;
            Scroll( -nDiffX, -nDiffY );
            getImpEditEngine().DelayedCallStatusHdl();
        }
    }

    // Cursor may trim a little ...
    if ( ( aEditCursor.Bottom() > GetVisDocTop() ) &&
         ( aEditCursor.Top() < GetVisDocBottom() ) )
    {
        if ( aEditCursor.Bottom() > GetVisDocBottom() )
            aEditCursor.SetBottom( GetVisDocBottom() );
        if ( aEditCursor.Top() < GetVisDocTop() )
            aEditCursor.SetTop( GetVisDocTop() );
    }

    const OutputDevice& rOutDev = GetOutputDevice();

    tools::Long nOnePixel = rOutDev.PixelToLogic( Size( 1, 0 ) ).Width();

    if ( ( aEditCursor.Top() + nOnePixel >= GetVisDocTop() ) &&
         ( aEditCursor.Bottom() - nOnePixel <= GetVisDocBottom() ) &&
         ( aEditCursor.Left() + nOnePixel >= GetVisDocLeft() ) &&
         ( aEditCursor.Right() - nOnePixel <= GetVisDocRight() ) )
    {
        tools::Rectangle aCursorRect = GetWindowPos( aEditCursor );
        Size aCursorSz( aCursorRect.GetSize() );
        // Rectangle is inclusive
        aCursorSz.AdjustWidth( -1 );
        aCursorSz.AdjustHeight( -1 );
        if ( !aCursorSz.Width() || !aCursorSz.Height() )
        {
            tools::Long nCursorSz = rOutDev.GetSettings().GetStyleSettings().GetCursorSize();
            nCursorSz = rOutDev.PixelToLogic( Size( nCursorSz, 0 ) ).Width();
            if ( !aCursorSz.Width() )
                aCursorSz.setWidth( nCursorSz );
            if ( !aCursorSz.Height() )
                aCursorSz.setHeight( nCursorSz );
        }
        // #111036# Let VCL do orientation for cursor, otherwise problem when cursor has direction flag
        if ( IsVertical() )
        {
            Size aOldSz( aCursorSz );
            aCursorSz.setWidth( aOldSz.Height() );
            aCursorSz.setHeight( aOldSz.Width() );
            // tdf#166602 the size is relative to the top right
            aCursorRect.AdjustLeft(aCursorRect.GetWidth() - aCursorSz.Width());
        }
        aCursorRect.SetSize(aCursorSz);
        return {{aCursorRect, aEditCursor, aShowCursorFlags, nTextPortionStart}};
    }

    return {};
}

void ImpEditView::ShowCursor( bool bGotoCursor, bool bForceVisCursor )
{
    // No ShowCursor in an empty View ...
    if (maOutputArea.IsEmpty())
        return;
    if ( (maOutputArea.Left() >= maOutputArea.Right() ) && ( maOutputArea.Top() >= maOutputArea.Bottom() ) )
        return;

    getEditEngine().CheckIdleFormatter();

    // For some reasons I end up here during the formatting, if the Outliner
    // is initialized in Paint, because no SetPool();
    if (getImpEditEngine().IsFormatting())
        return;
    if (!getImpEditEngine().IsUpdateLayout())
        return;
    if (getImpEditEngine().IsInUndo())
        return;

    if (mpOutputWindow && mpOutputWindow->GetCursor() != GetCursor())
        mpOutputWindow->SetCursor(GetCursor());

    EditPaM aPaM(maEditSelection.Max());

    sal_Int32 nPara = getEditEngine().GetEditDoc().GetPos( aPaM.GetNode() );
    if (nPara == EE_PARA_MAX) // #i94322
        return;

    ParaPortion const& rParaPortion = getEditEngine().GetParaPortions().getRef(nPara);

    auto const oCursor{ImplGetCursorRectAndMaybeScroll(aPaM, rParaPortion, bGotoCursor)};
    if (oCursor)
    {
        auto const [aCursorRect, aEditCursor, aShowCursorFlags, nTmp]{*oCursor};
        auto nTextPortionStart{nTmp}; // this one can't be const
        GetCursor()->SetPos( aCursorRect.TopLeft() );
        Size aCursorSz( aCursorRect.GetSize() );
        // #111036# Let VCL do orientation for cursor, otherwise problem when cursor has direction flag
        if ( IsVertical() )
        {
            GetCursor()->SetPos( aCursorRect.TopRight() );
            GetCursor()->SetOrientation( Degree10(IsTopToBottom() ? 2700 : 900) );
        }
        else
            // #i32593# Reset correct orientation in horizontal layout
            GetCursor()->SetOrientation();

        GetCursor()->SetSize( aCursorSz );

        if (comphelper::LibreOfficeKit::isActive() && mpViewShell && !mbSuppressLOKMessages)
        {
            Point aPos = GetCursor()->GetPos();
            boost::property_tree::ptree aMessageParams;
            const OutputDevice& rOutDev = GetOutputDevice();
            if (mpLOKSpecialPositioning)
            {
                // Sending the absolute (pure) logical coordinates of the cursor to the client is not
                // enough for it to accurately reconstruct the corresponding tile-twips coordinates of the cursor.
                // This is because the editeng(doc) positioning is not pixel aligned for each cell involved in the output-area
                // (it better not be!). A simple solution is to send the coordinates of a point ('refpoint') in the output-area
                // along with the relative position of the cursor w.r.t this chosen 'refpoint'.

                MapUnit eDevUnit = rOutDev.GetMapMode().GetMapUnit();
                tools::Rectangle aCursorRectPureLogical(aEditCursor.TopLeft(), GetCursor()->GetSize());
                // Get rectangle in window-coordinates from editeng(doc) coordinates in hmm.
                aCursorRectPureLogical = GetWindowPos(aCursorRectPureLogical);
                Point aRefPointLogical = GetOutputArea().TopLeft();
                // Get the relative coordinates w.r.t refpoint in display hmm.
                aCursorRectPureLogical.Move(-aRefPointLogical.X(), -aRefPointLogical.Y());
                if (getEditEngine().IsRightToLeft(nPara) || mpLOKSpecialPositioning->IsLayoutRTL())
                {
                    tools::Long nMirrorW = GetOutputArea().GetWidth();
                    tools::Long nLeft = aCursorRectPureLogical.Left(), nRight = aCursorRectPureLogical.Right();
                    aCursorRectPureLogical.SetLeft(nMirrorW - nRight);
                    aCursorRectPureLogical.SetRight(nMirrorW - nLeft);
                }
                // Convert to twips.
                aCursorRectPureLogical = OutputDevice::LogicToLogic(aCursorRectPureLogical, MapMode(eDevUnit), MapMode(MapUnit::MapTwip));
                // "refpoint" in print twips.
                const Point aRefPoint = mpLOKSpecialPositioning->GetRefPoint();
                aMessageParams.put("relrect", aCursorRectPureLogical.toString());
                aMessageParams.put("refpoint", aRefPoint.toString());
            }

            if (mpOutputWindow && mpOutputWindow->IsChart())
            {
                const vcl::Window* pViewShellWindow = mpViewShell->GetEditWindowForActiveOLEObj();
                if (pViewShellWindow && pViewShellWindow->IsAncestorOf(*mpOutputWindow))
                {
                    Point aOffsetPx = mpOutputWindow->GetOffsetPixelFrom(*pViewShellWindow);
                    Point aLogicOffset = mpOutputWindow->PixelToLogic(aOffsetPx);
                    aPos.Move(aLogicOffset.getX(), aLogicOffset.getY());
                }
            }

            tools::Rectangle aRect(aPos.getX(), aPos.getY(), aPos.getX() + GetCursor()->GetWidth(), aPos.getY() + GetCursor()->GetHeight());

            // LOK output is always in twips, convert from mm100 if necessary.
            if (rOutDev.GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
            {
                aRect = o3tl::convert(aRect, o3tl::Length::mm100, o3tl::Length::twip);
            }
            else if (rOutDev.GetMapMode().GetMapUnit() == MapUnit::MapTwip)
            {
                // Writer comments: they use editeng, but are separate widgets.
                Point aOrigin = rOutDev.GetMapMode().GetOrigin();
                // Move the rectangle, so that we output absolute twips.
                aRect.Move(aOrigin.getX(), aOrigin.getY());
            }
            // Let the LOK client decide the cursor width.
            aRect.setWidth(0);

            OString sRect = aRect.toString();
            aMessageParams.put("rectangle", sRect);

            SfxViewShell* pThisShell = dynamic_cast<SfxViewShell*>(mpViewShell);
            SfxViewShell* pOtherShell = dynamic_cast<SfxViewShell*>(mpOtherShell);
            assert(pThisShell);

            if (pOtherShell && pThisShell != pOtherShell)
            {
                // Another shell wants to know about our existing cursor.
                SfxLokHelper::notifyOtherView(pThisShell, pOtherShell,
                        LOK_CALLBACK_INVALIDATE_VIEW_CURSOR, aMessageParams);
            }
            else
            {
                // is cursor at a misspelled word ?
                uno::Reference<linguistic2::XSpellChecker1>  xSpeller(getImpEditEngine().GetSpeller());
                bool bIsWrong = xSpeller.is() && IsWrongSpelledWord(aPaM, /*bMarkIfWrong*/ false);
                EditView* pActiveView = GetEditViewPtr();

                boost::property_tree::ptree aHyperlinkTree;
                if (pActiveView && URLFieldHelper::IsCursorAtURLField(*pActiveView))
                {
                    if (const SvxFieldItem* pFld = GetField(aPos, nullptr, nullptr))
                        if (auto pUrlField = dynamic_cast<const SvxURLField*>(pFld->GetField()))
                            aHyperlinkTree = getHyperlinkPropTree(pUrlField->GetRepresentation(), pUrlField->GetURL());
                }
                else if (GetEditSelection().HasRange())
                {
                    if (pActiveView)
                    {
                        const SvxFieldItem* pFieldItem = pActiveView->GetFieldAtSelection();
                        if (pFieldItem)
                        {
                            const SvxFieldData* pField = pFieldItem->GetField();
                            if ( auto pUrlField = dynamic_cast<const SvxURLField*>( pField) )
                            {
                                aHyperlinkTree = getHyperlinkPropTree(pUrlField->GetRepresentation(), pUrlField->GetURL());
                            }
                        }
                    }
                }

                if (mbBroadcastLOKViewCursor)
                    SfxLokHelper::notifyOtherViews(pThisShell,
                            LOK_CALLBACK_INVALIDATE_VIEW_CURSOR, aMessageParams);

                aMessageParams.put("mispelledWord", bIsWrong ? 1 : 0);
                aMessageParams.add_child("hyperlink", aHyperlinkTree);

                if (comphelper::LibreOfficeKit::isViewIdForVisCursorInvalidation())
                    SfxLokHelper::notifyOtherView(pThisShell, pThisShell,
                            LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR, aMessageParams);
                else
                    pThisShell->libreOfficeKitViewCallback(LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR,
                            OString(aMessageParams.get<std::string>("rectangle")));
            }
        }

        CursorDirection nCursorDir = CursorDirection::NONE;
        if ( IsInsertMode() && !maEditSelection.HasRange() && (getImpEditEngine().HasDifferentRTLLevels(aPaM.GetNode()) ) )
        {
            sal_uInt16 nTextPortion = rParaPortion.GetTextPortions().FindPortion( aPaM.GetIndex(), nTextPortionStart, aShowCursorFlags.bPreferPortionStart);
            const TextPortion& rTextPortion = rParaPortion.GetTextPortions()[nTextPortion];
            if (rTextPortion.IsRightToLeft())
                nCursorDir = CursorDirection::RTL;
            else
                nCursorDir = CursorDirection::LTR;

        }
        GetCursor()->SetDirection( nCursorDir );

        if ( bForceVisCursor )
            GetCursor()->Show();
        {
            SvxFont aFont;
            getEditEngine().SeekCursor( aPaM.GetNode(), aPaM.GetIndex()+1, aFont );

            InputContext aInputContext(std::move(aFont), InputContextFlags::Text | InputContextFlags::ExtText);
            if (EditViewCallbacks* pCallbacks = getEditViewCallbacks())
                pCallbacks->EditViewInputContext(aInputContext);
            else if (auto xWindow = GetWindow())
                xWindow->SetInputContext(aInputContext);
        }
    }
    else
    {
        getImpEditEngine().GetStatus().GetStatusWord() = getImpEditEngine().GetStatus().GetStatusWord() | EditStatusFlags::CURSOROUT;
        GetCursor()->Hide();
        GetCursor()->SetPos( Point( -1, -1 ) );
        GetCursor()->SetSize( Size( 0, 0 ) );
    }
}

// call this so users of EditViewCallbacks can update their scrollbar state
// so called when we have either scrolled to a new location
// or the size of document has changed
void ImpEditView::ScrollStateChange()
{
    if (EditViewCallbacks* pCallbacks = getEditViewCallbacks())
        pCallbacks->EditViewScrollStateChange();
}

Pair ImpEditView::Scroll( tools::Long ndX, tools::Long ndY, ScrollRangeCheck nRangeCheck )
{
    DBG_ASSERT(getImpEditEngine().IsFormatted(), "Scroll: Not formatted!");
    if ( !ndX && !ndY )
        return Pair( 0, 0 );

    const OutputDevice& rOutDev = GetOutputDevice();

#ifdef DBG_UTIL
    tools::Rectangle aR(maOutputArea);
    aR = rOutDev.LogicToPixel( aR );
    aR = rOutDev.PixelToLogic( aR );
    SAL_WARN_IF(aR != maOutputArea, "editeng""OutArea before Scroll not aligned");
#endif

    tools::Rectangle aNewVisArea( GetVisDocArea() );

    // Vertical:
    if ( !IsVertical() )
    {
        aNewVisArea.AdjustTop( -ndY );
        aNewVisArea.AdjustBottom( -ndY );
    }
    else
    {
        if( IsTopToBottom() )
        {
            aNewVisArea.AdjustTop(ndX );
            aNewVisArea.AdjustBottom(ndX );
        }
        else
        {
            aNewVisArea.AdjustTop( -ndX );
            aNewVisArea.AdjustBottom( -ndX );
        }
    }
    if ( ( nRangeCheck == ScrollRangeCheck::PaperWidthTextSize ) && ( aNewVisArea.Bottom() > static_cast<tools::Long>(getImpEditEngine().GetTextHeight()) ) )
    {
        // GetTextHeight still optimizing!
        tools::Long nDiff = getImpEditEngine().GetTextHeight() - aNewVisArea.Bottom(); // negative
        aNewVisArea.Move( 0, nDiff );   // could end up in the negative area...
    }
    if ( aNewVisArea.Top() < 0 )
        aNewVisArea.Move( 0, -aNewVisArea.Top() );

    // Horizontal:
    if ( !IsVertical() )
    {
        aNewVisArea.AdjustLeft( -ndX );
        aNewVisArea.AdjustRight( -ndX );
    }
    else
    {
        if (IsTopToBottom())
        {
            aNewVisArea.AdjustLeft( -ndY );
            aNewVisArea.AdjustRight( -ndY );
        }
        else
        {
            aNewVisArea.AdjustLeft(ndY );
            aNewVisArea.AdjustRight(ndY );
        }
    }
    if ( ( nRangeCheck == ScrollRangeCheck::PaperWidthTextSize ) && ( aNewVisArea.Right() > static_cast<tools::Long>(getImpEditEngine().CalcTextWidth( false )) ) )
    {
        tools::Long nDiff = getImpEditEngine().CalcTextWidth( false ) - aNewVisArea.Right();     // negative
        aNewVisArea.Move( nDiff, 0 );   // could end up in the negative area...
    }
    if ( aNewVisArea.Left() < 0 )
        aNewVisArea.Move( -aNewVisArea.Left(), 0 );

    // The difference must be alignt on pixel (due to scroll!)
    tools::Long nDiffX = !IsVertical() ? ( GetVisDocLeft() - aNewVisArea.Left() ) : (IsTopToBottom() ? -( GetVisDocTop() - aNewVisArea.Top() ) : (GetVisDocTop() - aNewVisArea.Top()));
    tools::Long nDiffY = !IsVertical() ? ( GetVisDocTop() - aNewVisArea.Top() ) : (IsTopToBottom() ? (GetVisDocLeft() - aNewVisArea.Left()) : -(GetVisDocTop() - aNewVisArea.Top()));

    Size aDiffs( nDiffX, nDiffY );
    aDiffs = rOutDev.LogicToPixel( aDiffs );
    aDiffs = rOutDev.PixelToLogic( aDiffs );

    tools::Long nRealDiffX = aDiffs.Width();
    tools::Long nRealDiffY = aDiffs.Height();


    if ( nRealDiffX || nRealDiffY )
    {
        vcl::Cursor* pCrsr = GetCursor();
        bool bVisCursor = pCrsr->IsVisible();
        pCrsr->Hide();
        if (mpOutputWindow)
            mpOutputWindow->PaintImmediately();
        if (!IsVertical())
            maVisDocStartPos.Move(-nRealDiffX, -nRealDiffY);
        else
        {
            if (IsTopToBottom())
                maVisDocStartPos.Move(-nRealDiffY, nRealDiffX);
            else
                maVisDocStartPos.Move(nRealDiffY, -nRealDiffX);
        }
        // Move by aligned value does not necessarily result in aligned
        // rectangle ...
        maVisDocStartPos = rOutDev.LogicToPixel(maVisDocStartPos);
        maVisDocStartPos = rOutDev.PixelToLogic(maVisDocStartPos);
        tools::Rectangle aRect(maOutputArea);

        if (mpOutputWindow)
        {
            mpOutputWindow->Scroll( nRealDiffX, nRealDiffY, aRect, ScrollFlags::Clip );
        }

        if (comphelper::LibreOfficeKit::isActive() || getEditViewCallbacks())
        {
            // Need to invalidate the window, otherwise no tile will be re-painted.
            GetEditViewPtr()->Invalidate();
        }

        if (mpOutputWindow)
            mpOutputWindow->PaintImmediately();
        pCrsr->SetPos( pCrsr->GetPos() + Point( nRealDiffX, nRealDiffY ) );
        if ( bVisCursor )
        {
            tools::Rectangle aCursorRect( pCrsr->GetPos(), pCrsr->GetSize() );
            if (maOutputArea.Contains(aCursorRect))
                pCrsr->Show();
        }

        if (getImpEditEngine().GetNotifyHdl().IsSet())
        {
            EENotify aNotify( EE_NOTIFY_TEXTVIEWSCROLLED );
            getImpEditEngine().GetNotifyHdl().Call( aNotify );
        }

        if (EditViewCallbacks* pCallbacks = getEditViewCallbacks())
            pCallbacks->EditViewScrollStateChange();

        if (comphelper::LibreOfficeKit::isActive())
        {
            DrawSelectionXOR();
        }
    }

    return Pair( nRealDiffX, nRealDiffY );
}

uno::Reference<datatransfer::clipboard::XClipboard> ImpEditView::GetClipboard() const
{
    if (EditViewCallbacks* pCallbacks = getEditViewCallbacks())
        return pCallbacks->GetClipboard();
    if (vcl::Window* pWindow = GetWindow())
        return pWindow->GetClipboard();
    SAL_WARN("editeng""falling back to using GetSystemClipboard");
    return GetSystemClipboard();
}

bool ImpEditView::PostKeyEvent( const KeyEvent& rKeyEvent, vcl::Window const * pFrameWin )
{
    bool bDone = false;

    KeyFuncType eFunc = rKeyEvent.GetKeyCode().GetFunction();
    if ( eFunc != KeyFuncType::DONTKNOW )
    {
        switch ( eFunc )
        {
            case KeyFuncType::CUT:
            {
                if (!mbReadOnly)
                {
                    uno::Reference<datatransfer::clipboard::XClipboard> aClipBoard(GetClipboard());
                    CutCopy( aClipBoard, true );
                    bDone = true;
                }
            }
            break;
            case KeyFuncType::COPY:
            {
                uno::Reference<datatransfer::clipboard::XClipboard> aClipBoard(GetClipboard());
                CutCopy( aClipBoard, false );
                bDone = true;
            }
            break;
            case KeyFuncType::PASTE:
            {
                if (!mbReadOnly && IsPasteEnabled())
                {
--> --------------------

--> maximum size reached

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

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

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