Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/sc/source/ui/view/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 102 kB image not shown  

Quelle  gridwin4.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 <memory>
#include <scitems.hxx>
#include <editeng/eeitem.hxx>

#include <svtools/colorcfg.hxx>
#include <editeng/colritem.hxx>
#include <editeng/editview.hxx>
#include <editeng/fhgtitem.hxx>
#include <editeng/brushitem.hxx>
#include <sfx2/bindings.hxx>
#include <sfx2/printer.hxx>
#include <vcl/cursor.hxx>
#include <vcl/settings.hxx>
#include <o3tl/unit_conversion.hxx>
#include <osl/diagnose.h>
#include <tools/UnitConversion.hxx>
#include <tools/weakbase.hxx>

#include <LibreOfficeKit/LibreOfficeKitEnums.h>
#include <comphelper/lok.hxx>
#include <sfx2/lokhelper.hxx>
#include <sfx2/lokcomponenthelpers.hxx>
#include <officecfg/Office/Calc.hxx>

#include <svx/svdview.hxx>
#include <svx/svdpagv.hxx>
#include <svx/sdrpagewindow.hxx>
#include <svx/sdr/contact/objectcontactofpageview.hxx>
#include <svx/sdr/contact/viewobjectcontact.hxx>
#include <svx/sdr/contact/viewcontact.hxx>
#include <tabvwsh.hxx>
#include <vcl/lineinfo.hxx>
#include <vcl/sysdata.hxx>

#include <gridwin.hxx>
#include <viewdata.hxx>
#include <output.hxx>
#include <document.hxx>
#include <attrib.hxx>
#include <patattr.hxx>
#include <dbdata.hxx>
#include <notemark.hxx>
#include <dbfunc.hxx>
#include <scmod.hxx>
#include <inputhdl.hxx>
#include <rfindlst.hxx>
#include <hiranges.hxx>
#include <pagedata.hxx>
#include <globstr.hrc>
#include <scresid.hxx>
#include <docsh.hxx>
#include <cbutton.hxx>
#include <invmerge.hxx>
#include <editutil.hxx>
#include <inputopt.hxx>
#include <fillinfo.hxx>
#include <dpcontrol.hxx>
#include <queryparam.hxx>
#include <queryentry.hxx>
#include <markdata.hxx>
#include <sc.hrc>
#include <vcl/virdev.hxx>
#include <svx/sdrpaintwindow.hxx>
#include <drwlayer.hxx>

static void lcl_LimitRect( tools::Rectangle& rRect, const tools::Rectangle& rVisible )
{
    if ( rRect.Top()    < rVisible.Top()-1 )    rRect.SetTop( rVisible.Top()-1 );
    if ( rRect.Bottom() > rVisible.Bottom()+1 ) rRect.SetBottom( rVisible.Bottom()+1 );

    // The header row must be drawn also when the inner rectangle is not visible,
    // that is why there is no return value anymore.
    // When it is far away, then lcl_DrawOneFrame is not even called.
}

static void lcl_DrawOneFrame( vcl::RenderContext* pDev, const tools::Rectangle& rInnerPixel,
                        const OUString& rTitle, const Color& rColor, bool bTextBelow,
                        double nPPTX, double nPPTY, const Fraction& rZoomY,
                        const ScDocument& rDoc, ScViewData& rButtonViewData, bool bLayoutRTL )
{
    // rButtonViewData is only used to set the button size,

    tools::Rectangle aInner = rInnerPixel;
    if ( bLayoutRTL )
    {
        aInner.SetLeft( rInnerPixel.Right() );
        aInner.SetRight( rInnerPixel.Left() );
    }

    tools::Rectangle aVisible( Point(0,0), pDev->GetOutputSizePixel() );
    lcl_LimitRect( aInner, aVisible );

    tools::Rectangle aOuter = aInner;
    tools::Long nHor = static_cast<tools::Long>( SC_SCENARIO_HSPACE * nPPTX );
    tools::Long nVer = static_cast<tools::Long>( SC_SCENARIO_VSPACE * nPPTY );
    aOuter.AdjustLeft( -nHor );
    aOuter.AdjustRight(nHor );
    aOuter.AdjustTop( -nVer );
    aOuter.AdjustBottom(nVer );

    //  use ScPatternAttr::GetFont only for font size
    vcl::Font aAttrFont;
    rDoc.getCellAttributeHelper().getDefaultCellAttribute().fillFontOnly(aAttrFont, pDev, &rZoomY);

    //  everything else from application font
    vcl::Font aAppFont = pDev->GetSettings().GetStyleSettings().GetAppFont();
    aAppFont.SetFontSize( aAttrFont.GetFontSize() );

    aAppFont.SetAlignment( ALIGN_TOP );
    pDev->SetFont( aAppFont );

    Size aTextSize( pDev->GetTextWidth( rTitle ), pDev->GetTextHeight() );

    if ( bTextBelow )
        aOuter.AdjustBottom(aTextSize.Height() );
    else
        aOuter.AdjustTop( -(aTextSize.Height()) );

    pDev->SetLineColor();
    pDev->SetFillColor( rColor );
    //  left, top, right, bottom
    pDev->DrawRect( tools::Rectangle( aOuter.Left(),  aOuter.Top(),    aInner.Left(),  aOuter.Bottom() ) );
    pDev->DrawRect( tools::Rectangle( aOuter.Left(),  aOuter.Top(),    aOuter.Right(), aInner.Top()    ) );
    pDev->DrawRect( tools::Rectangle( aInner.Right(), aOuter.Top(),    aOuter.Right(), aOuter.Bottom() ) );
    pDev->DrawRect( tools::Rectangle( aOuter.Left(),  aInner.Bottom(), aOuter.Right(), aOuter.Bottom() ) );

    tools::Long nButtonY = bTextBelow ? aInner.Bottom() : aOuter.Top();

    ScDDComboBoxButton aComboButton(pDev);
    aComboButton.SetOptSizePixel();
    tools::Long nBWidth  = tools::Long(aComboButton.GetSizePixel().Width() * rZoomY);
    tools::Long nBHeight = nVer + aTextSize.Height() + 1;
    Size aButSize( nBWidth, nBHeight );
    tools::Long nButtonPos = bLayoutRTL ? aOuter.Left() : aOuter.Right()-nBWidth+1;
    aComboButton.Draw( Point(nButtonPos, nButtonY), aButSize );
    rButtonViewData.SetScenButSize( aButSize );

    tools::Long nTextStart = bLayoutRTL ? aInner.Right() - aTextSize.Width() + 1 : aInner.Left();

    bool bWasClip = false;
    vcl::Region aOldClip;
    bool bClip = ( aTextSize.Width() > aOuter.Right() - nBWidth - aInner.Left() );
    if ( bClip )
    {
        if (pDev->IsClipRegion())
        {
            bWasClip = true;
            aOldClip = pDev->GetActiveClipRegion();
        }
        tools::Long nClipStartX = bLayoutRTL ? aOuter.Left() + nBWidth : aInner.Left();
        tools::Long nClipEndX = bLayoutRTL ? aInner.Right() : aOuter.Right() - nBWidth;
        pDev->SetClipRegion( vcl::Region(tools::Rectangle( nClipStartX, nButtonY + nVer/2,
                            nClipEndX, nButtonY + nVer/2 + aTextSize.Height())) );
    }

    pDev->DrawText( Point( nTextStart, nButtonY + nVer/2 ), rTitle );

    if ( bClip )
    {
        if ( bWasClip )
            pDev->SetClipRegion(aOldClip);
        else
            pDev->SetClipRegion();
    }

    pDev->SetFillColor();
    pDev->SetLineColor( COL_BLACK );
    pDev->DrawRect( aInner );
    pDev->DrawRect( aOuter );
}

static void lcl_DrawScenarioFrames( OutputDevice* pDev, ScViewData& rViewData, ScSplitPos eWhich,
                            SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2 )
{
    ScDocument& rDoc = rViewData.GetDocument();
    SCTAB nTab = rViewData.GetTabNo();
    SCTAB nTabCount = rDoc.GetTableCount();
    if ( nTab+1 >= nTabCount || !rDoc.IsScenario(nTab+1) || rDoc.IsScenario(nTab) )
        return;

    if ( nX1 > 0 ) --nX1;
    if ( nY1>=2 ) nY1 -= 2;             // Hack: Header row affects two cells
    else if ( nY1 > 0 ) --nY1;
    if ( nX2 < rDoc.MaxCol() ) ++nX2;
    if ( nY2 < rDoc.MaxRow()-1 ) nY2 += 2;     // Hack: Header row affects two cells
    else if ( nY2 < rDoc.MaxRow() ) ++nY2;
    ScRange aViewRange( nX1,nY1,nTab, nX2,nY2,nTab );

    //! cache the ranges in table!!!!

    ScMarkData aMarks(rDoc.GetSheetLimits());
    for (SCTAB i=nTab+1; i<nTabCount && rDoc.IsScenario(i); i++)
        rDoc.MarkScenario( i, nTab, aMarks, false, ScScenarioFlags::ShowFrame );
    ScRangeListRef xRanges = new ScRangeList;
    aMarks.FillRangeListWithMarks( xRanges.get(), false );

    bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
    tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;

    for (size_t j = 0, n = xRanges->size(); j < n; ++j)
    {
        ScRange aRange = (*xRanges)[j];
        // Always extend scenario frame to merged cells where no new non-covered cells
        // are framed
        rDoc.ExtendTotalMerge( aRange );

        //! -> Extend repaint when merging !!!

        if ( aRange.Intersects( aViewRange ) )          //! Space for Text/Button?
        {
            Point aStartPos = rViewData.GetScrPos(
                                aRange.aStart.Col(), aRange.aStart.Row(), eWhich, true );
            Point aEndPos = rViewData.GetScrPos(
                                aRange.aEnd.Col()+1, aRange.aEnd.Row()+1, eWhich, true );
            //  on the grid:
            aStartPos.AdjustX( -nLayoutSign );
            aStartPos.AdjustY( -1 );
            aEndPos.AdjustX( -nLayoutSign );
            aEndPos.AdjustY( -1 );

            bool bTextBelow = ( aRange.aStart.Row() == 0 );

            OUString aCurrent;
            Color aColor( COL_LIGHTGRAY );
            for (SCTAB nAct=nTab+1; nAct<nTabCount && rDoc.IsScenario(nAct); nAct++)
                if ( rDoc.IsActiveScenario(nAct) && rDoc.HasScenarioRange(nAct,aRange) )
                {
                    OUString aDummyComment;
                    ScScenarioFlags nDummyFlags;
                    rDoc.GetName( nAct, aCurrent );
                    rDoc.GetScenarioData( nAct, aDummyComment, aColor, nDummyFlags );
                }

            if (aCurrent.isEmpty())
                aCurrent = ScResId( STR_EMPTYDATA );

            //! Own text "(None)" instead of "(Empty)" ???

            lcl_DrawOneFrame( pDev, tools::Rectangle( aStartPos, aEndPos ),
                                aCurrent, aColor, bTextBelow,
                                rViewData.GetPPTX(), rViewData.GetPPTY(), rViewData.GetZoomY(),
                                rDoc, rViewData, bLayoutRTL );
        }
    }
}

static void lcl_DrawHighlight( ScOutputData& rOutputData, const ScViewData& rViewData,
                        const std::vector<ScHighlightEntry>& rHighlightRanges )
{
    SCTAB nTab = rViewData.GetTabNo();
    for ( const auto& rHighlightRange : rHighlightRanges)
    {
        ScRange aRange = rHighlightRange.aRef;
        if ( nTab >= aRange.aStart.Tab() && nTab <= aRange.aEnd.Tab() )
        {
            rOutputData.DrawRefMark(
                                aRange.aStart.Col(), aRange.aStart.Row(),
                                aRange.aEnd.Col(), aRange.aEnd.Row(),
                                rHighlightRange.aColor, false );
        }
    }
}

void ScGridWindow::DoInvertRect( const tools::Rectangle& rPixel )
{
    if ( rPixel == aInvertRect )
        aInvertRect = tools::Rectangle();      // Cancel
    else
    {
        OSL_ENSURE( aInvertRect.IsEmpty(), "DoInvertRect no pairs" );

        aInvertRect = rPixel;           // Mark new rectangle
    }

    UpdateHeaderOverlay();      // uses aInvertRect
}

void ScGridWindow::PrePaint(vcl::RenderContext& /*rRenderContext*/)
{
    // forward PrePaint to DrawingLayer
    ScTabViewShell* pTabViewShell = mrViewData.GetViewShell();

    if(pTabViewShell)
    {
        SdrView* pDrawView = pTabViewShell->GetScDrawView();

        if (pDrawView)
        {
            pDrawView->PrePaint();
        }
    }
}

bool ScGridWindow::NeedLOKCursorInvalidation(const tools::Rectangle& rCursorRect,
        const Fraction aScaleX, const Fraction aScaleY)
{
    // Don't see the need for a map as there will be only a few zoom levels
    // and as of now X and Y zooms in online are the same.
    for (auto& rEntry : maLOKLastCursor)
    {
        if (aScaleX == rEntry.aScaleX && aScaleY == rEntry.aScaleY)
        {
            if (rCursorRect == rEntry.aRect)
                return false// No change

            // Update and allow invalidate.
            rEntry.aRect = rCursorRect;
            return true;
        }
    }

    maLOKLastCursor.push_back(LOKCursorEntry{aScaleX, aScaleY, rCursorRect});
    return true;
}

void ScGridWindow::InvalidateLOKViewCursor(const tools::Rectangle& rCursorRect,
        const Fraction aScaleX, const Fraction aScaleY)
{
    if (!NeedLOKCursorInvalidation(rCursorRect, aScaleX, aScaleY))
        return;

    ScTabViewShell* pThisViewShell = mrViewData.GetViewShell();
    SfxViewShell* pViewShell = SfxViewShell::GetFirst();

    while (pViewShell)
    {
        if (pViewShell != pThisViewShell && pViewShell->GetDocId() == pThisViewShell->GetDocId())
        {
            ScTabViewShell* pOtherViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
            if (pOtherViewShell)
            {
                ScViewData& rOtherViewData = pOtherViewShell->GetViewData();
                Fraction aZoomX = rOtherViewData.GetZoomX();
                Fraction aZoomY = rOtherViewData.GetZoomY();
                if (aZoomX == aScaleX && aZoomY == aScaleY)
                {
                    SfxLokHelper::notifyOtherView(pThisViewShell, pOtherViewShell,
                            LOK_CALLBACK_INVALIDATE_VIEW_CURSOR, "rectangle", rCursorRect.toString());
                }
            }
        }

        pViewShell = SfxViewShell::GetNext(*pViewShell);
    }
}

void ScGridWindow::Paint( vcl::RenderContext& /*rRenderContext*/, const tools::Rectangle& rRect )
{
    ScDocument& rDoc = mrViewData.GetDocument();
    if ( rDoc.IsInInterpreter() )
    {
        // Via Reschedule, interpreted cells do not trigger Invalidate again,
        // otherwise for instance an error box would never appear (bug 36381).
        // Later, through bNeedsRepaint everything is painted again.
        if ( bNeedsRepaint )
        {
            //! Merge Rectangle?
            aRepaintPixel = tools::Rectangle();            // multiple -> paint all
        }
        else
        {
            bNeedsRepaint = true;
            aRepaintPixel = LogicToPixel(rRect);    // only affected ranges
        }
        return;
    }

    // #i117893# If GetSizePixel needs to call the resize handler, the resulting nested Paint call
    // (possibly for a larger rectangle) has to be allowed. Call GetSizePixel before setting bIsInPaint.
    GetSizePixel();

    if (bIsInPaint)
        return;

    bIsInPaint = true;

    tools::Rectangle aPixRect = LogicToPixel( rRect );

    SCCOL nX1 = mrViewData.GetPosX(eHWhich);
    SCROW nY1 = mrViewData.GetPosY(eVWhich);

    SCTAB nTab = mrViewData.GetTabNo();

    double nPPTX = mrViewData.GetPPTX();
    double nPPTY = mrViewData.GetPPTY();

    tools::Rectangle aMirroredPixel = aPixRect;
    if ( rDoc.IsLayoutRTL( nTab ) )
    {
        //  mirror and swap
        tools::Long nWidth = GetSizePixel().Width();
        aMirroredPixel.SetLeft( nWidth - 1 - aPixRect.Right() );
        aMirroredPixel.SetRight( nWidth - 1 - aPixRect.Left() );
    }

    tools::Long nScrX = ScViewData::ToPixel( rDoc.GetColWidth( nX1, nTab ), nPPTX );
    while ( nScrX <= aMirroredPixel.Left() && nX1 < rDoc.MaxCol() )
    {
        ++nX1;
        nScrX += ScViewData::ToPixel( rDoc.GetColWidth( nX1, nTab ), nPPTX );
    }
    SCCOL nX2 = nX1;
    while ( nScrX <= aMirroredPixel.Right() && nX2 < rDoc.MaxCol() )
    {
        ++nX2;
        nScrX += ScViewData::ToPixel( rDoc.GetColWidth( nX2, nTab ), nPPTX );
    }

    tools::Long nScrY = 0;
    ScViewData::AddPixelsWhile( nScrY, aPixRect.Top(), nY1, rDoc.MaxRow(), nPPTY, &rDoc, nTab);
    SCROW nY2 = nY1;
    if (nScrY <= aPixRect.Bottom() && nY2 < rDoc.MaxRow())
    {
        ++nY2;
        ScViewData::AddPixelsWhile( nScrY, aPixRect.Bottom(), nY2, rDoc.MaxRow(), nPPTY, &rDoc, nTab);
    }

    Draw( nX1,nY1,nX2,nY2, ScUpdateMode::Marks ); // don't continue with painting

    bIsInPaint = false;
}

void ScGridWindow::Resize()
{
    // tdf#159348 update highlight overlay when window is resized
    if (officecfg::Office::Calc::Content::Display::ColumnRowHighlighting::get())
        UpdateHighlightOverlay();
}

void ScGridWindow::Draw( SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2, ScUpdateMode eMode )
{
    ScDocument& rDoc = mrViewData.GetDocument();

    // let's ignore the normal Draw() attempts when doing the tiled rendering,
    // all the rendering should go through PaintTile() in that case.
    // TODO revisit if we can actually turn this into an assert(), and clean
    // up the callers
    if (comphelper::LibreOfficeKit::isActive())
        return;

    bool bTextWysiwyg = ScModule::get()->GetInputOptions().GetTextWysiwyg();

    if (mrViewData.IsMinimized())
        return;

    PutInOrder( nX1, nX2 );
    PutInOrder( nY1, nY2 );

    OSL_ENSURE( rDoc.ValidCol(nX2) && rDoc.ValidRow(nY2), "GridWin Draw area too big" );

    UpdateVisibleRange();

    if (nX2 < maVisibleRange.mnCol1 || nY2 < maVisibleRange.mnRow1)
        return;
    // invisible
    if (nX1 < maVisibleRange.mnCol1)
        nX1 = maVisibleRange.mnCol1;
    if (nY1 < maVisibleRange.mnRow1)
        nY1 = maVisibleRange.mnRow1;

    if (nX1 > maVisibleRange.mnCol2 || nY1 > maVisibleRange.mnRow2)
        return;

    if (nX2 > maVisibleRange.mnCol2)
        nX2 = maVisibleRange.mnCol2;
    if (nY2 > maVisibleRange.mnRow2)
        nY2 = maVisibleRange.mnRow2;

    if ( eMode != ScUpdateMode::Marks && nX2 < maVisibleRange.mnCol2)
        nX2 = maVisibleRange.mnCol2;  // to continue painting

    // point of no return

    ++nPaintCount; // mark that painting is in progress

    SCTAB nTab = mrViewData.GetTabNo();
    rDoc.ExtendHidden( nX1, nY1, nX2, nY2, nTab );

    Point aScrPos = mrViewData.GetScrPos( nX1, nY1, eWhich );
    tools::Long nMirrorWidth = GetSizePixel().Width();
    bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
    if ( bLayoutRTL )
    {
        tools::Long nEndPixel = mrViewData.GetScrPos( nX2+1, maVisibleRange.mnRow1, eWhich ).X();
        nMirrorWidth = aScrPos.X() - nEndPixel;
        aScrPos.setX( nEndPixel + 1 );
    }

    tools::Long nScrX = aScrPos.X();
    tools::Long nScrY = aScrPos.Y();

    SCCOL nCurX = mrViewData.GetCurX();
    SCROW nCurY = mrViewData.GetCurY();
    SCCOL nCurEndX = nCurX;
    SCROW nCurEndY = nCurY;
    rDoc.ExtendMerge( nCurX, nCurY, nCurEndX, nCurEndY, nTab );
    bool bCurVis = nCursorHideCount==0 &&
                    ( nCurEndX+1 >= nX1 && nCurX <= nX2+1 && nCurEndY+1 >= nY1 && nCurY <= nY2+1 );

    //  AutoFill Handles
    if ( !bCurVis && nCursorHideCount==0 && bAutoMarkVisible && aAutoMarkPos.Tab() == nTab &&
            ( aAutoMarkPos.Col() != nCurX || aAutoMarkPos.Row() != nCurY ) )
    {
        SCCOL nHdlX = aAutoMarkPos.Col();
        SCROW nHdlY = aAutoMarkPos.Row();
        rDoc.ExtendMerge( nHdlX, nHdlY, nHdlX, nHdlY, nTab );
        // left and top is unaffected

        //! Paint AutoFill handles alone (without Cursor) ???
    }

    double nPPTX = mrViewData.GetPPTX();
    double nPPTY = mrViewData.GetPPTY();

    const ScViewOptions& rOpts = mrViewData.GetOptions();

    // data block

    ScTableInfo aTabInfo(nY1, nY2, true);
    rDoc.FillInfo( aTabInfo, nX1, nY1, nX2, nY2, nTab,
                   nPPTX, nPPTY, false, rOpts.GetOption(VOPT_FORMULAS),
                   &mrViewData.GetMarkData() );

    Fraction aZoomX = mrViewData.GetZoomX();
    Fraction aZoomY = mrViewData.GetZoomY();
    ScOutputData aOutputData( GetOutDev(), OUTTYPE_WINDOW, aTabInfo, &rDoc, nTab,
                               nScrX, nScrY, nX1, nY1, nX2, nY2, nPPTX, nPPTY,
                               &aZoomX, &aZoomY );

    aOutputData.SetMirrorWidth( nMirrorWidth ); // needed for RTL
    aOutputData.SetSpellCheckContext(mpSpellCheckCxt.get());

    ScopedVclPtr< VirtualDevice > xFmtVirtDev;
    bool bLogicText = bTextWysiwyg; // call DrawStrings in logic MapMode?

    if ( bTextWysiwyg )
    {
        //  use printer for text formatting

        OutputDevice* pFmtDev = rDoc.GetPrinter();
        pFmtDev->SetMapMode( mrViewData.GetLogicMode(eWhich) );
        aOutputData.SetFmtDevice( pFmtDev );
    }
    else if ( aZoomX != aZoomY && mrViewData.IsOle() )
    {
        //  #i45033# For OLE inplace editing with different zoom factors,
        //  use a virtual device with 1/100th mm as text formatting reference

        xFmtVirtDev.disposeAndReset( VclPtr<VirtualDevice>::Create() );
        xFmtVirtDev->SetMapMode(MapMode(MapUnit::Map100thMM));
        aOutputData.SetFmtDevice( xFmtVirtDev.get() );

        bLogicText = true// use logic MapMode
    }

    DrawContent(*GetOutDev(), aTabInfo, aOutputData, bLogicText);

    // If something was inverted during the Paint (selection changed from Basic Macro)
    // then this is now mixed up and has to be repainted
    OSL_ENSURE(nPaintCount, "Wrong nPaintCount");
    --nPaintCount;
    if (!nPaintCount)
        CheckNeedsRepaint();

    // Flag drawn formula cells "unchanged".
    rDoc.ResetChanged(ScRange(nX1, nY1, nTab, nX2, nY2, nTab));
    rDoc.PrepareFormulaCalc();
}

namespace {

class SuppressEditViewMessagesGuard
{
public:
    SuppressEditViewMessagesGuard(EditView& rEditView) :
        mrEditView(rEditView),
        mbOrigSuppressFlag(rEditView.IsSuppressLOKMessages())
    {
        if (!mbOrigSuppressFlag)
            mrEditView.SuppressLOKMessages(true);
    }

    ~SuppressEditViewMessagesGuard()
    {
        if (mrEditView.IsSuppressLOKMessages() != mbOrigSuppressFlag)
            mrEditView.SuppressLOKMessages(mbOrigSuppressFlag);
    }

private:
    EditView& mrEditView;
    const bool mbOrigSuppressFlag;
};

}

/**
 * Used to store the necessary information about the (combined-)tile
 * area relevant to coordinate transformations in RTL mode.
 */

class ScLokRTLContext
{
public:
    ScLokRTLContext(const ScOutputData& rOutputData, const tools::Long nTileDeviceOriginPixelX):
        mrOutputData(rOutputData),
        mnTileDevOriginX(nTileDeviceOriginPixelX)
    {}

    /**
     * Converts from document x pixel position to the
     * corresponding pixel position w.r.t the tile device origin.
     */

    tools::Long docToTilePos(tools::Long nPosX) const
    {
        tools::Long nMirrorX = (-2 * mnTileDevOriginX) + mrOutputData.GetScrW();
        return nMirrorX - 1 - nPosX;
    }


private:
    const ScOutputData& mrOutputData;
    const tools::Long mnTileDevOriginX;
};

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

tools::Long GetSide(const tools::Rectangle& rRect, int i)
{
    static decltype(&tools::Rectangle::Left) GetSides[4] = {
        &tools::Rectangle::Left, &tools::Rectangle::Top,
        &tools::Rectangle::Right, &tools::Rectangle::Bottom
    };
    return (rRect.*GetSides[i])();
}

Fraction GetZoom(const ScViewData& rViewData, int i)
{
    static decltype(&ScViewData::GetZoomX) GetZooms[4] = {
        &ScViewData::GetZoomX, &ScViewData::GetZoomY,
        &ScViewData::GetZoomX, &ScViewData::GetZoomY
    };
    return (rViewData.*GetZooms[i])();
}
}

void ScGridWindow::DrawContent(OutputDevice &rDevice, const ScTableInfo& rTableInfo, ScOutputData& aOutputData,
        bool bLogicText)
{
    ScModule* pScMod = ScModule::get();
    ScDocument& rDoc = mrViewData.GetDocument();
    const ScViewOptions& rOpts = mrViewData.GetOptions();
    bool bIsTiledRendering = comphelper::LibreOfficeKit::isActive();
    bool bNoBackgroundAndGrid = bIsTiledRendering
                                && comphelper::LibreOfficeKit::isCompatFlagSet(
                                       comphelper::LibreOfficeKit::Compat::scNoGridBackground);

    SCTAB nTab = aOutputData.nTab;
    SCCOL nX1 = aOutputData.nX1;
    SCROW nY1 = aOutputData.nY1;
    SCCOL nX2 = aOutputData.nX2;
    SCROW nY2 = aOutputData.nY2;
    tools::Long nScrX = aOutputData.nScrX;
    tools::Long nScrY = aOutputData.nScrY;

    const svtools::ColorConfig& rColorCfg = pScMod->GetColorConfig();
    Color aGridColor( rColorCfg.GetColorValue( svtools::CALCGRID ).nColor );
    if ( aGridColor == COL_TRANSPARENT )
    {
        //  use view options' grid color only if color config has "automatic" color
        aGridColor = rOpts.GetGridColor();
    }

    ScTabViewShell* pCurTabViewShell = mrViewData.GetViewShell();

    aOutputData.SetSyntaxMode       ( mrViewData.IsSyntaxMode() );
    aOutputData.SetGridColor        ( aGridColor );
    aOutputData.SetShowNullValues   ( rOpts.GetOption( VOPT_NULLVALS ) );
    aOutputData.SetShowFormulas     ( rOpts.GetOption( VOPT_FORMULAS ) );
    aOutputData.SetShowSpellErrors  ( pCurTabViewShell && pCurTabViewShell->IsAutoSpell() );
    aOutputData.SetMarkClipped      ( pScMod->GetColorConfig().GetColorValue(svtools::CALCTEXTOVERFLOW).bIsVisible );

    aOutputData.SetUseStyleColor( true );       // always set in table view

    aOutputData.SetViewShell(pCurTabViewShell);

    bool bGrid = rOpts.GetOption( VOPT_GRID ) && mrViewData.GetShowGrid();
    bool bGridFirst = !rOpts.GetOption( VOPT_GRID_ONTOP );

    bool bPage = rOpts.GetOption( VOPT_PAGEBREAKS ) && !bIsTiledRendering;

    bool bPageMode = mrViewData.IsPagebreakMode();
    if (bPageMode)                                      // after FindChanged
    {
        // SetPagebreakMode also initializes bPrinted Flags
        aOutputData.SetPagebreakMode( mrViewData.GetView()->GetPageBreakData() );
    }

    EditView*   pEditView = nullptr;
    bool        bEditMode = mrViewData.HasEditView(eWhich);
    if ( bEditMode && mrViewData.GetRefTabNo() == nTab )
    {
        SCCOL nEditCol;
        SCROW nEditRow;
        mrViewData.GetEditView( eWhich, pEditView, nEditCol, nEditRow );
        SCCOL nEditEndCol = mrViewData.GetEditEndCol();
        SCROW nEditEndRow = mrViewData.GetEditEndRow();


        if (officecfg::Office::Calc::Content::Display::EditCellBackgroundHighlighting::get())
        {
            Color aDocColor = pScMod->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor;
            if (!getViewData().GetMarkData().IsMarked() && mrViewData.GetEditHighlight())
            {
                Color aHighlightColor = pScMod->GetColorConfig().GetColorValue(svtools::CALCCELLFOCUS).nColor;
                aHighlightColor.Merge(aDocColor, 100);
                aDocColor = aHighlightColor;
            }

            Color aBackColor = rDoc.GetPattern(nEditCol, nEditRow, getViewData().GetTabNo())->GetItem(ATTR_BACKGROUND).GetColor();
            if (!aBackColor.IsTransparent())
                aDocColor = aBackColor;

            pEditView->SetBackgroundColor(aDocColor);
        }

        if ( nEditEndCol >= nX1 && nEditCol <= nX2 && nEditEndRow >= nY1 && nEditRow <= nY2 )
            aOutputData.SetEditCell( nEditCol, nEditRow );
        else
            bEditMode = false;
    }

    const MapMode aOriginalMode = rDevice.GetMapMode();

    // define drawing layer map mode and paint rectangle
    MapMode aDrawMode = GetDrawMapMode();
    if (bIsTiledRendering)
    {
        // FIXME this shouldn't be necessary once we change the entire Calc to
        // work in the logic coordinates (ideally 100ths of mm - so that it is
        // the same as editeng and drawinglayer), and get rid of all the
        // SetMapMode's and other unnecessary fun we have with pixels
        // See also ScGridWindow::GetDrawMapMode() for the rest of this hack
        aDrawMode.SetOrigin(PixelToLogic(Point(nScrX, nScrY), aDrawMode));
    }
    tools::Rectangle aDrawingRectLogic;
    bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
    bool bLokRTL = bLayoutRTL && bIsTiledRendering;
    std::unique_ptr<ScLokRTLContext> pLokRTLCtxt(
        bLokRTL ?
            new ScLokRTLContext(aOutputData, o3tl::convert(aOriginalMode.GetOrigin().X(), o3tl::Length::twip, o3tl::Length::px)) :
            nullptr);

    {
        // get drawing pixel rect
        tools::Rectangle aDrawingRectPixel(
            bLokRTL ? Point(-(nScrX + aOutputData.GetScrW()), nScrY) : Point(nScrX, nScrY),
            Size(aOutputData.GetScrW(), aOutputData.GetScrH()));

        // correct for border (left/right)
        if(rDoc.MaxCol() == nX2 && !bLokRTL)
        {
            if(bLayoutRTL)
            {
                aDrawingRectPixel.SetLeft( 0 );
            }
            else
            {
                aDrawingRectPixel.SetRight( GetOutputSizePixel().getWidth() );
            }
        }

        // correct for border (bottom)
        if(rDoc.MaxRow() == nY2)
        {
            aDrawingRectPixel.SetBottom( GetOutputSizePixel().getHeight() );
        }

        // get logic positions
        aDrawingRectLogic = PixelToLogic(aDrawingRectPixel, aDrawMode);
    }

    bool bInPlaceEditing = bEditMode && (mrViewData.GetRefTabNo() == mrViewData.GetTabNo());
    vcl::Cursor* pInPlaceCrsr = nullptr;
    bool bInPlaceVisCursor = false;
    if (bInPlaceEditing)
    {
        // toggle the cursor off if it's on to ensure the cursor invert
        // background logic remains valid after the background is cleared on
        // the next cursor flash
        pInPlaceCrsr = pEditView->GetCursor();
        bInPlaceVisCursor = pInPlaceCrsr && pInPlaceCrsr->IsVisible();
        if (bInPlaceVisCursor)
            pInPlaceCrsr->Hide();
    }

    OutputDevice* pContentDev = &rDevice;   // device for document content, used by overlay manager
    SdrPaintWindow* pTargetPaintWindow = nullptr; // #i74769# work with SdrPaintWindow directly

    {
        // init redraw
        if (pCurTabViewShell)
        {
            MapMode aCurrentMapMode(pContentDev->GetMapMode());
            pContentDev->SetMapMode(aDrawMode);
            SdrView* pDrawView = pCurTabViewShell->GetScDrawView();

            if(pDrawView)
            {
                // #i74769# Use new BeginDrawLayers() interface
                vcl::Region aDrawingRegion(aDrawingRectLogic);
                pTargetPaintWindow = pDrawView->BeginDrawLayers(pContentDev, aDrawingRegion);
                OSL_ENSURE(pTargetPaintWindow, "BeginDrawLayers: Got no SdrPaintWindow (!)");

                if (!bIsTiledRendering)
                {
                    // #i74769# get target device from SdrPaintWindow, this may be the prerender
                    // device now, too.
                    pContentDev = &(pTargetPaintWindow->GetTargetOutputDevice());
                    aOutputData.SetContentDevice(pContentDev);
                }
            }

            pContentDev->SetMapMode(aCurrentMapMode);
        }
    }

    // app-background / document edge (area) (Pixel)
    if ( !bIsTiledRendering && ( nX2 == rDoc.MaxCol() || nY2 == rDoc.MaxRow() ) )
    {
        // save MapMode and set to pixel
        MapMode aCurrentMapMode(pContentDev->GetMapMode());
        pContentDev->SetMapMode(MapMode(MapUnit::MapPixel));

        tools::Rectangle aPixRect( Point(), GetOutputSizePixel() );
        pContentDev->SetFillColor( rColorCfg.GetColorValue(svtools::APPBACKGROUND).nColor );
        pContentDev->SetLineColor();
        if ( nX2==rDoc.MaxCol() )
        {
            tools::Rectangle aDrawRect( aPixRect );
            if ( bLayoutRTL )
                aDrawRect.SetRight( nScrX - 1 );
            else
                aDrawRect.SetLeft( nScrX + aOutputData.GetScrW() );
            if (aDrawRect.Right() >= aDrawRect.Left())
                pContentDev->DrawRect( aDrawRect );
        }
        if ( nY2==rDoc.MaxRow() )
        {
            tools::Rectangle aDrawRect( aPixRect );
            aDrawRect.SetTop( nScrY + aOutputData.GetScrH() );
            if ( nX2==rDoc.MaxCol() )
            {
                // no double painting of the corner
                if ( bLayoutRTL )
                    aDrawRect.SetLeft( nScrX );
                else
                    aDrawRect.SetRight( nScrX + aOutputData.GetScrW() - 1 );
            }
            if (aDrawRect.Bottom() >= aDrawRect.Top())
                pContentDev->DrawRect( aDrawRect );
        }

        // restore MapMode
        pContentDev->SetMapMode(aCurrentMapMode);
    }

    if ( rDoc.HasBackgroundDraw( nTab, aDrawingRectLogic ) )
    {
        pContentDev->SetMapMode(MapMode(MapUnit::MapPixel));
        aOutputData.DrawClear();

            // drawing background

        pContentDev->SetMapMode(aDrawMode);
        DrawRedraw( aOutputData, SC_LAYER_BACK );
    }
    else
        aOutputData.SetSolidBackground(!bNoBackgroundAndGrid);

    aOutputData.DrawDocumentBackground();

    if (bGridFirst && (bGrid || bPage))
    {
        // Draw lines in background color cover over lok client grid lines in merged cell areas if bNoBackgroundAndGrid is set.
        if (bNoBackgroundAndGrid)
            aOutputData.DrawGrid(*pContentDev, false /* bGrid */, false /* bPage */, true /* bMergeCover */);
        else
            aOutputData.DrawGrid(*pContentDev, bGrid, bPage);
    }

    aOutputData.DrawBackground(*pContentDev);

    if (!bGridFirst && (bGrid || bPage) && !bNoBackgroundAndGrid)
        aOutputData.DrawGrid(*pContentDev, bGrid, bPage);

    pContentDev->SetMapMode(MapMode(MapUnit::MapPixel));

    //tdf#128258 - draw a dotted line before hidden columns/rows
    DrawHiddenIndicator(nX1,nY1,nX2,nY2, *pContentDev);

    if ( bPageMode )
    {
        // DrawPagePreview draws complete lines/page numbers, must always be clipped
        if ( aOutputData.SetChangedClip() )
        {
            DrawPagePreview(nX1,nY1,nX2,nY2, *pContentDev);
            pContentDev->SetClipRegion();
        }
    }

    aOutputData.DrawShadow();
    aOutputData.DrawFrame(*pContentDev);

    aOutputData.DrawSparklines(*pContentDev);

    // Show Note Mark
    if ( rOpts.GetOption( VOPT_NOTES ) )
        aOutputData.DrawNoteMarks(*pContentDev);

    if ( rOpts.GetOption( VOPT_FORMULAS_MARKS ) )
        aOutputData.DrawFormulaMarks(*pContentDev);

    if ( !bLogicText )
        aOutputData.DrawStrings();     // in pixel MapMode

    // edit cells and printer-metrics text must be before the buttons
    // (DataPilot buttons contain labels in UI font)

    pContentDev->SetMapMode(mrViewData.GetLogicMode(eWhich));
    if ( bLogicText )
        aOutputData.DrawStrings(true);      // in logic MapMode if bLogicText is set
    aOutputData.DrawEdit(true);

    // the buttons are painted in absolute coordinates
    if (bIsTiledRendering)
    {
        // Tiled offset nScrX, nScrY
        MapMode aMap( MapUnit::MapPixel );
        Point aOrigin(o3tl::convert(aOriginalMode.GetOrigin(), o3tl::Length::twip, o3tl::Length::px));
        aOrigin.Move(nScrX, nScrY);
        aMap.SetOrigin(aOrigin);
        pContentDev->SetMapMode(aMap);
    }
    else
        pContentDev->SetMapMode(MapMode(MapUnit::MapPixel));

    // Autofilter- and Pivot-Buttons
    DrawButtons(nX1, nX2, rTableInfo, pContentDev, pLokRTLCtxt.get());          // Pixel

    pContentDev->SetMapMode(MapMode(MapUnit::MapPixel));

    aOutputData.DrawClipMarks();

    // In any case, Scenario / ChangeTracking must happen after DrawGrid, also for !bGridFirst

    //! test if ChangeTrack display is active
    //! Disable scenario frame via view option?

    SCTAB nTabCount = rDoc.GetTableCount();
    const std::vector<ScHighlightEntry> &rHigh = mrViewData.GetView()->GetHighlightRanges();
    bool bHasScenario = ( nTab+1<nTabCount && rDoc.IsScenario(nTab+1) && !rDoc.IsScenario(nTab) );
    bool bHasChange = ( rDoc.GetChangeTrack() != nullptr );

    if ( bHasChange || bHasScenario || !rHigh.empty() )
    {
        //! Merge SetChangedClip() with DrawMarks() ?? (different MapMode!)

        if ( bHasChange )
            aOutputData.DrawChangeTrack();

        if ( bHasScenario )
            lcl_DrawScenarioFrames( pContentDev, mrViewData, eWhich, nX1,nY1,nX2,nY2 );

        lcl_DrawHighlight( aOutputData, mrViewData, rHigh );
    }

        // Drawing foreground

    pContentDev->SetMapMode(aDrawMode);

    // Bitmaps and buttons are in absolute pixel coordinates.
    const MapMode aOrig = pContentDev->GetMapMode();
    if (bIsTiledRendering)
    {
        Point aOrigin(o3tl::convert(aOriginalMode.GetOrigin(), o3tl::Length::twip, o3tl::Length::px));
        tools::Long nXOffset = bLayoutRTL ? -aOrigin.getX() + aOutputData.GetScrW()
                                          : aOrigin.getX();
        Size aPixelOffset(nXOffset, aOrigin.getY());
        pContentDev->SetPixelOffset(aPixelOffset);
        comphelper::LibreOfficeKit::setLocalRendering();
    }

    DrawRedraw( aOutputData, SC_LAYER_FRONT );
    DrawRedraw( aOutputData, SC_LAYER_INTERN );
    DrawSdrGrid( aDrawingRectLogic, pContentDev );

    if (bIsTiledRendering)
    {
        pContentDev->SetPixelOffset(Size());
        pContentDev->SetMapMode(aOrig);
    }

    pContentDev->SetMapMode(MapMode(MapUnit::MapPixel));

    if ( mrViewData.IsRefMode() && nTab >= mrViewData.GetRefStartZ() && nTab <= mrViewData.GetRefEndZ() )
    {
        Color aRefColor( rColorCfg.GetColorValue(svtools::CALCREFERENCE).nColor );
        aOutputData.DrawRefMark( mrViewData.GetRefStartX(), mrViewData.GetRefStartY(),
                                mrViewData.GetRefEndX(), mrViewData.GetRefEndY(),
                                aRefColor, false );
    }

        // range finder

    ScInputHandler* pHdl = pScMod->GetInputHdl( mrViewData.GetViewShell() );
    if (pHdl)
    {
        ScDocShell& rDocSh = mrViewData.GetDocShell();
        ScRangeFindList* pRangeFinder = pHdl->GetRangeFindList();
        if ( pRangeFinder && !pRangeFinder->IsHidden() &&
                pRangeFinder->GetDocName() == rDocSh.GetTitle() )
        {
            sal_uInt16 nCount = static_cast<sal_uInt16>(pRangeFinder->Count());
            for (sal_uInt16 i=0; i<nCount; i++)
            {
                ScRangeFindData& rData = pRangeFinder->GetObject(i);

                ScRange aRef = rData.aRef;
                aRef.PutInOrder();
                if ( aRef.aStart.Tab() >= nTab && aRef.aEnd.Tab() <= nTab )
                    aOutputData.DrawRefMark( aRef.aStart.Col(), aRef.aStart.Row(),
                                            aRef.aEnd.Col(), aRef.aEnd.Row(),
                                            rData.nColor,
                                            true );
            }
        }
    }

    {
        // end redraw
        if (pCurTabViewShell)
        {
            MapMode aCurrentMapMode(pContentDev->GetMapMode());
            pContentDev->SetMapMode(aDrawMode);

            if (bIsTiledRendering)
            {
                Point aOrigin = aOriginalMode.GetOrigin();
                if (bLayoutRTL)
                    aOrigin.setX(-aOrigin.getX()
                                 + o3tl::toTwips(aOutputData.nScrX + aOutputData.GetScrW(), o3tl::Length::px));
                else
                    aOrigin.AdjustX(o3tl::toTwips(aOutputData.nScrX, o3tl::Length::px));

                aOrigin.AdjustY(o3tl::toTwips(aOutputData.nScrY, o3tl::Length::px));
                aOrigin = o3tl::convert(aOrigin, o3tl::Length::twip, o3tl::Length::mm100);
                // keep into account the zoom factor
                aOrigin = aOrigin.scale(
                    aDrawMode.GetScaleX().GetDenominator(), aDrawMode.GetScaleX().GetNumerator(),
                    aDrawMode.GetScaleY().GetDenominator(), aDrawMode.GetScaleY().GetNumerator());

                MapMode aNew = rDevice.GetMapMode();
                aNew.SetOrigin(aOrigin);
                rDevice.SetMapMode(aNew);
            }

            SdrView* pDrawView = pCurTabViewShell->GetScDrawView();

            if(pDrawView)
            {
                // #i74769# work with SdrPaintWindow directly
                pDrawView->EndDrawLayers(*pTargetPaintWindow, true);
            }

            pContentDev->SetMapMode(aCurrentMapMode);
        }
    }

    // in place editing - lok case
    if (bIsTiledRendering)
    {
        ScTabViewShell* pThisViewShell = mrViewData.GetViewShell();
        ViewShellList aCurrentDocViewList = LOKEditViewHistory::GetSortedViewsForDoc(pThisViewShell->GetDocId());
        tools::Rectangle aTileRectPx(Point(nScrX, nScrY), Size(aOutputData.GetScrW(), aOutputData.GetScrH()));

        for (SfxViewShell* pVS: aCurrentDocViewList)
        {
            auto pTabViewShell = dynamic_cast<ScTabViewShell*>(pVS);
            if (!pTabViewShell)
                continue;

            ScViewData& rOtherViewData = pTabViewShell->GetViewData();
            ScSplitPos eOtherWhich = rOtherViewData.GetEditActivePart();

            bool bOtherEditMode = rOtherViewData.HasEditView(eOtherWhich);
            SCCOL nCol1 = rOtherViewData.GetEditStartCol();
            SCROW nRow1 = rOtherViewData.GetEditStartRow();
            SCCOL nCol2 = rOtherViewData.GetEditEndCol();
            SCROW nRow2 = rOtherViewData.GetEditEndRow();

            if (!(bOtherEditMode
                  && ( nCol2 >= nX1 && nCol1 <= nX2 && nRow2 >= nY1 && nRow1 <= nY2 )
                  && rOtherViewData.GetRefTabNo() == nTab))
                continue// only views where in place editing is occurring need to be rendered

            EditView* pOtherEditView = rOtherViewData.GetEditView(eOtherWhich);
            if (!pOtherEditView)
                continue;

            rDevice.SetLineColor();
            // Theme colors
            const ScPatternAttr* pPattern = rDoc.GetPattern( nCol1, nRow1, nTab );
            Color aCellColor = pPattern->GetItem(ATTR_BACKGROUND).GetColor();
            if (aCellColor.IsTransparent())
            {
                if (ScTabViewShell* pCurrentViewShell = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current()))
                {
                    const ScViewRenderingOptions& rViewRenderingOptions = pCurrentViewShell->GetViewRenderingData();
                    aCellColor = rViewRenderingOptions.GetDocColor();
                }
            }
            rDevice.SetFillColor(aCellColor);
            pOtherEditView->SetBackgroundColor(aCellColor);

            // edit rectangle / background
            Point aStart = mrViewData.GetScrPos( nCol1, nRow1, eOtherWhich );
            Point aEnd = mrViewData.GetScrPos( nCol2+1, nRow2+1, eOtherWhich );
            tools::Rectangle aEditRectPx(aStart, aEnd);
            if (bLokRTL)
            {
                // Transform the cell range X coordinates such that the edit cell area is
                // horizontally mirrored w.r.t the (combined-)tile.
                aStart.setX(pLokRTLCtxt->docToTilePos(aStart.X()));
                aEnd.setX(pLokRTLCtxt->docToTilePos(aEnd.X()));
            }

            // don't overwrite grid
            tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
            aEnd.AdjustX( -(2 * nLayoutSign) );
            aEnd.AdjustY( -2 );

            tools::Rectangle aBackground(aStart, aEnd);
            if (bLokRTL)
                aBackground.Normalize();
            tools::Rectangle aBGAbs(aBackground);

            // Need to draw the background in absolute coords.
            Point aOriginTw = aOriginalMode.GetOrigin();
            Point aOriginAbsTw = aOriginTw + o3tl::toTwips(aTileRectPx.GetPos(), o3tl::Length::px);
            Point aOriginAbsPx = o3tl::convert(aOriginAbsTw, o3tl::Length::twip, o3tl::Length::px);
            aBackground += aOriginAbsPx;
            rDevice.SetMapMode(aDrawMode);

            // keep into account the zoom factor
            Point aNewOrigin(o3tl::convert(aOriginAbsTw, o3tl::Length::twip, o3tl::Length::mm100));
            aNewOrigin = aNewOrigin.scale(
                aDrawMode.GetScaleX().GetDenominator(), aDrawMode.GetScaleX().GetNumerator(),
                aDrawMode.GetScaleY().GetDenominator(), aDrawMode.GetScaleY().GetNumerator());

            MapMode aNewMM = rDevice.GetMapMode();
            aNewMM.SetOrigin(aNewOrigin);
            rDevice.SetMapMode(aNewMM);

            // paint the background
            rDevice.DrawRect(rDevice.PixelToLogic(aBackground));

            OutputDevice& rOtherWin = pOtherEditView->GetOutputDevice();
            const MapMode aOrigMapMode = rOtherWin.GetMapMode();
            const o3tl::Length aOrigOutputAreaUnit = MapToO3tlLength(aOrigMapMode.GetMapUnit());

            // paint text
            const tools::Rectangle aOrigOutputArea(pOtherEditView->GetOutputArea()); // Not in pixels.

            tools::Rectangle aNewOutputArea;
            // compute output area for view with a different zoom level wrt the view used for painting
            if (!(mrViewData.GetZoomX() == rOtherViewData.GetZoomX() &&
                  mrViewData.GetZoomY() == rOtherViewData.GetZoomY()))
            {
                Point aOtherStart = rOtherViewData.GetScrPos( nCol1, nRow1, eOtherWhich );
                Point aOtherEnd = rOtherViewData.GetScrPos( nCol2+1, nRow2+1, eOtherWhich );
                tools::Rectangle aOtherEditRect(
                    o3tl::convert(tools::Rectangle(aOtherStart, aOtherEnd), o3tl::Length::px,
                                  aOrigOutputAreaUnit));

                tools::Long sides[4];
                for (auto i: {0, 1, 2, 3})
                {
                    const Fraction zoomThis = ::GetZoom(mrViewData, i);
                    const Fraction zoomOther = ::GetZoom(rOtherViewData, i);

                    const auto unscaledOtherEditRectSide
                        = o3tl::convert(GetSide(aOtherEditRect, i),
                                        zoomOther.GetDenominator(), zoomOther.GetNumerator());

                    const auto scaledAdd
                        = o3tl::convert(GetSide(aOrigOutputArea, i) - unscaledOtherEditRectSide,
                                        zoomThis.GetNumerator(), zoomThis.GetDenominator());

                    sides[i] = GetSide(aEditRectPx, i)
                               + o3tl::convert(scaledAdd, aOrigOutputAreaUnit, o3tl::Length::px);
                }

                aNewOutputArea = tools::Rectangle(sides[0], sides[1], sides[2], sides[3]);
                aNewOutputArea += aOriginAbsPx;
            }
            // compute output area for RTL case
            if (bLokRTL)
            {
                if (aNewOutputArea.IsEmpty())
                {
                    // same zoom level as view used for painting
                    aNewOutputArea = rDevice.LogicToPixel(aOrigOutputArea);
                }
                // a small workaround for getting text position matching cursor position horizontally.
                const tools::Long nCursorGapPx = 2;
                // Transform the cell range X coordinates such that the edit cell area is
                // horizontally mirrored w.r.t the (combined-)tile.
                aNewOutputArea = tools::Rectangle(
                    pLokRTLCtxt->docToTilePos(aNewOutputArea.Left() - aOriginAbsPx.X()) + aOriginAbsPx.X(),
                    aNewOutputArea.Top(),
                    pLokRTLCtxt->docToTilePos(aNewOutputArea.Right() - aOriginAbsPx.X()) + aOriginAbsPx.X() + nCursorGapPx,
                    aNewOutputArea.Bottom());
                aNewOutputArea.Normalize();
            }

            if (aNewOutputArea.IsEmpty())
            {
                // same zoom level and not RTL: no need to change the output area before painting
                pOtherEditView->Paint(rDevice.PixelToLogic(aTileRectPx), &rDevice);
            }
            else
            {
                // EditView has an 'output area' which is used to clip the 'paint area' we provide below.
                // So they need to be in the same coordinates/units. This is tied to the mapmode of the gridwin
                // attached to the EditView, so we have to change its mapmode too (temporarily). We save the
                // original mapmode and 'output area' and roll them back when we finish painting to rDevice.
                rOtherWin.SetMapMode(rDevice.GetMapMode());

                // Avoid sending wrong cursor/selection messages by the 'other' view, as the output-area is going
                // to be tweaked temporarily to match the current view's zoom.
                SuppressEditViewMessagesGuard aGuard(*pOtherEditView);

                pOtherEditView->SetOutputArea(rDevice.PixelToLogic(aNewOutputArea));
                pOtherEditView->Paint(rDevice.PixelToLogic(aTileRectPx), &rDevice);

                // EditView will do the cursor notifications correctly if we're in
                // print-twips messaging mode.
                if (pTabViewShell == pThisViewShell
                    && !comphelper::LibreOfficeKit::isCompatFlagSet(
                        comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
                {
                    // Now we need to get relative cursor position within the editview.
                    // This is for sending the pixel-aligned twips position of the cursor to the specific views with
                    // the same given zoom level.
                    tools::Rectangle aCursorRect = pEditView->GetEditCursor();
                    Point aCursPos = o3tl::toTwips(aCursorRect.TopLeft(), o3tl::Length::mm100);

                    const MapMode& rDevMM = rDevice.GetMapMode();
                    MapMode aMM(MapUnit::MapTwip);
                    aMM.SetScaleX(rDevMM.GetScaleX());
                    aMM.SetScaleY(rDevMM.GetScaleY());

                    aBGAbs.AdjustLeft(1);
                    aBGAbs.AdjustTop(1);
                    aCursorRect = GetOutDev()->PixelToLogic(aBGAbs, aMM);
                    aCursorRect.setWidth(0);
                    aCursorRect.Move(aCursPos.getX(), 0);
                    // Sends view cursor position to views of all matching zooms if needed (avoids duplicates).
                    InvalidateLOKViewCursor(aCursorRect, aMM.GetScaleX(), aMM.GetScaleY());
                }

                // Rollback the mapmode and 'output area'.
                rOtherWin.SetMapMode(aOrigMapMode);
                pOtherEditView->SetOutputArea(aOrigOutputArea);
            }
            rDevice.SetMapMode(MapMode(MapUnit::MapPixel));
        }
    }

    // In-place editing - when the user is typing, we need to paint the text
    // using the editeng.
    // It's being done after EndDrawLayers() to get it outside the overlay
    // buffer and on top of everything.
    if (bInPlaceEditing && !bIsTiledRendering && bInPlaceVisCursor)
        pInPlaceCrsr->Show();

    if (mrViewData.HasEditView(eWhich))
    {
        // set MapMode for text edit
        rDevice.SetMapMode(mrViewData.GetLogicMode());
    }
    else
        rDevice.SetMapMode(aDrawMode);

    if (bPage && bInitialPageBreaks)
        SetupInitialPageBreaks(rDoc, nTab);
}


void ScGridWindow::SetupInitialPageBreaks(const ScDocument& rDoc, SCTAB nTab)
{
    // tdf#124983, if option LibreOfficeDev Calc/View/Visual Aids/Page breaks
    // is enabled, breaks should be visible. If the document is opened the first
    // time, the breaks are not calculated yet, so for this initialization
    // a timer will be triggered here.
    std::set<SCCOL> aColBreaks;
    std::set<SCROW> aRowBreaks;
    rDoc.GetAllColBreaks(aColBreaks, nTab, truefalse);
    rDoc.GetAllRowBreaks(aRowBreaks, nTab, truefalse);
    if (aColBreaks.empty() || aRowBreaks.empty())
    {
        maShowPageBreaksTimer.SetPriority(TaskPriority::DEFAULT_IDLE);
        maShowPageBreaksTimer.Start();
    }
    bInitialPageBreaks = false;
}

namespace
{
    template<typename IndexType>
    void lcl_getBoundingRowColumnforTile(ScViewData& rViewData,
            tools::Long nTileStartPosPx, tools::Long nTileEndPosPx,
            sal_Int32& nTopLeftTileOffset, sal_Int32& nTopLeftTileOrigin,
            sal_Int32& nTopLeftTileIndex, sal_Int32& nBottomRightTileIndex)
    {
        const bool bColumnHeader = std::is_same<IndexType, SCCOL>::value;

        SCTAB nTab = rViewData.GetTabNo();

        IndexType nStartIndex = -1;
        IndexType nEndIndex = -1;
        tools::Long nStartPosPx = 0;
        tools::Long nEndPosPx = 0;

        ScPositionHelper& rPositionHelper =
                bColumnHeader ? rViewData.GetLOKWidthHelper() : rViewData.GetLOKHeightHelper();
        const auto& rStartNearest = rPositionHelper.getNearestByPosition(nTileStartPosPx);
        const auto& rEndNearest = rPositionHelper.getNearestByPosition(nTileEndPosPx);

        ScBoundsProvider aBoundsProvider(rViewData, nTab, bColumnHeader);
        aBoundsProvider.Compute(rStartNearest, rEndNearest, nTileStartPosPx, nTileEndPosPx);
        aBoundsProvider.GetStartIndexAndPosition(nStartIndex, nStartPosPx); ++nStartIndex;
        aBoundsProvider.GetEndIndexAndPosition(nEndIndex, nEndPosPx);

        nTopLeftTileOffset = nTileStartPosPx - nStartPosPx;
        nTopLeftTileOrigin = nStartPosPx;
        nTopLeftTileIndex = nStartIndex;
        nBottomRightTileIndex = nEndIndex;
    }

    void lcl_RTLAdjustTileColOffset(ScViewData& rViewData, sal_Int32& nTileColOffset,
        tools::Long nTileEndPx, sal_Int32 nEndCol, SCTAB nTab,
        const ScDocument& rDoc, double fPPTX)
    {
        auto GetColWidthPx = [&rDoc, nTab, fPPTX](SCCOL nCol) {
            const sal_uInt16 nSize = rDoc.GetColWidth(nCol, nTab);
            const tools::Long nSizePx = ScViewData::ToPixel(nSize, fPPTX);
            return nSizePx;
        };

        ScPositionHelper rHelper = rViewData.GetLOKWidthHelper();
        tools::Long nEndColPos = rHelper.computePosition(nEndCol, GetColWidthPx);

        nTileColOffset += (nEndColPos - nTileEndPx - nTileColOffset);
    }

    class ScLOKProxyObjectContact final : public sdr::contact::ObjectContactOfPageView
    {
    private:
        tools::WeakReference<ScDrawView> m_xScDrawView;

    public:
        explicit ScLOKProxyObjectContact(
            ScDrawView* pDrawView,
            SdrPageWindow& rPageWindow,
            const char* pDebugName) :
            ObjectContactOfPageView(rPageWindow, pDebugName),
            m_xScDrawView(pDrawView)
        {
        }

        virtual bool supportsGridOffsets() const override { return true; }

        virtual void calculateGridOffsetForViewObjectContact(
            basegfx::B2DVector& rTarget,
            const sdr::contact::ViewObjectContact& rClient) const override
        {
            ScDrawView* pScDrawView = m_xScDrawView.get();
            if (!pScDrawView)
                return;

            SdrPageView* pPageView(pScDrawView->GetSdrPageView());
            if (!pPageView)
                return;

            SdrPageWindow* pSdrPageWindow = nullptr;
            if (pPageView->PageWindowCount() > 0)
                pSdrPageWindow = pPageView->GetPageWindow(0);
            if (!pSdrPageWindow)
                return;

            sdr::contact::ObjectContact& rObjContact(pSdrPageWindow->GetObjectContact());

            SdrObject* pTargetSdrObject(rClient.GetViewContact().TryToGetSdrObject());
            if (pTargetSdrObject)
                rTarget = pTargetSdrObject->GetViewContact().GetViewObjectContact(rObjContact).getGridOffset();
        }
    };

    class ScLOKDrawView : public FmFormView
    {
    public:
        ScLOKDrawView(OutputDevice* pOut, ScViewData& rData) :
            FmFormView(*rData.GetDocument().GetDrawLayer(), pOut),
            mpScDrawView(rData.GetScDrawView())
        {
        }

        virtual sdr::contact::ObjectContact* createViewSpecificObjectContact(
                SdrPageWindow& rPageWindow, const char* pDebugName) const override
        {
            if (!mpScDrawView)
                return SdrView::createViewSpecificObjectContact(rPageWindow, pDebugName);

            return new ScLOKProxyObjectContact(mpScDrawView, rPageWindow, pDebugName);
        }

    private:
        ScDrawView* mpScDrawView;
    };
// anonymous namespace

void ScGridWindow::resetCachedViewGridOffsets() const
{
    if (mpLOKDrawView)
        if (SdrPageView* pPageView = mpLOKDrawView->GetSdrPageView())
            pPageView->resetGridOffsetsOfAllPageWindows();
}

void ScGridWindow::PaintTile( VirtualDevice& rDevice,
                              int nOutputWidth, int nOutputHeight,
                              int nTilePosX, int nTilePosY,
                              tools::Long nTileWidth, tools::Long nTileHeight,
                              SCCOL nTiledRenderingAreaEndCol, SCROW nTiledRenderingAreaEndRow )
{
    Fraction origZoomX = mrViewData.GetZoomX();
    Fraction origZoomY = mrViewData.GetZoomY();

    // Output size is in pixels while tile position and size are in logical units (twips).

    // Assumption: always paint the whole sheet i.e. "visible" range is always
    // from (0,0) to last data position.

    // Tile geometry is independent of the zoom level, but the output size is
    // dependent of the zoom level.  Determine the correct zoom level before
    // we start.

    // FIXME the painting works using a mixture of drawing with coordinates in
    // pixels and in logic coordinates; it should be cleaned up to use logic
    // coords only, and avoid all the SetMapMode()'s.
    // Similarly to Writer, we should set the mapmode once on the rDevice, and
    // not care about any zoom settings.

    Fraction aFracX(o3tl::convert(nOutputWidth, o3tl::Length::px, o3tl::Length::twip), nTileWidth);
    Fraction aFracY(o3tl::convert(nOutputHeight, o3tl::Length::px, o3tl::Length::twip), nTileHeight);

    const bool bChangeZoom = (aFracX !=  origZoomX || aFracY != origZoomY);

    // page break zoom, and aLogicMode in ScViewData
    // FIXME: there are issues when SetZoom is called conditionally.
    mrViewData.SetZoom(aFracX, aFracY, true);
    if (bChangeZoom)
    {
        if (ScDrawView* pDrawView = mrViewData.GetScDrawView())
            pDrawView->resetGridOffsetsForAllSdrPageViews();
    }

    const double fTilePosXPixel = static_cast<double>(nTilePosX) * nOutputWidth / nTileWidth;
    const double fTilePosYPixel = static_cast<double>(nTilePosY) * nOutputHeight / nTileHeight;
    const double fTileBottomPixel = static_cast<double>(nTilePosY + nTileHeight) * nOutputHeight / nTileHeight;
    const double fTileRightPixel = static_cast<double>(nTilePosX + nTileWidth) * nOutputWidth / nTileWidth;

    SCTAB nTab = mrViewData.GetTabNo();
    ScDocument& rDoc = mrViewData.GetDocument();

    const double fPPTX = mrViewData.GetPPTX();
    const double fPPTY = mrViewData.GetPPTY();

    // find approximate col/row offsets of nearby.
    sal_Int32 nTopLeftTileRowOffset = 0;
    sal_Int32 nTopLeftTileColOffset = 0;
    sal_Int32 nTopLeftTileRowOrigin = 0;
    sal_Int32 nTopLeftTileColOrigin = 0;

    sal_Int32 nTopLeftTileRow = 0;
    sal_Int32 nTopLeftTileCol = 0;
    sal_Int32 nBottomRightTileRow = 0;
    sal_Int32 nBottomRightTileCol = 0;

    lcl_getBoundingRowColumnforTile<SCROW>(mrViewData,
            fTilePosYPixel, fTileBottomPixel,
            nTopLeftTileRowOffset, nTopLeftTileRowOrigin,
            nTopLeftTileRow, nBottomRightTileRow);

    lcl_getBoundingRowColumnforTile<SCCOL>(mrViewData,
            fTilePosXPixel, fTileRightPixel,
            nTopLeftTileColOffset, nTopLeftTileColOrigin,
            nTopLeftTileCol, nBottomRightTileCol);

    // Enlarge
    nBottomRightTileCol++;
    nBottomRightTileRow++;

    if (nTopLeftTileCol > rDoc.MaxCol())
        nTopLeftTileCol = rDoc.MaxCol();

    if (nBottomRightTileCol > rDoc.MaxCol())
        nBottomRightTileCol = rDoc.MaxCol();

    if (nTopLeftTileRow > MAXTILEDROW)
        nTopLeftTileRow = MAXTILEDROW;

    if (nBottomRightTileRow > MAXTILEDROW)
        nBottomRightTileRow = MAXTILEDROW;

    bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );

    if (bLayoutRTL)
    {
        lcl_RTLAdjustTileColOffset(mrViewData, nTopLeftTileColOffset,
            fTileRightPixel, nBottomRightTileCol, nTab,
            rDoc, fPPTX);
    }

    // size of the document including drawings, charts, etc.
    SCCOL nEndCol = nTiledRenderingAreaEndCol;
    SCROW nEndRow = nTiledRenderingAreaEndRow;

    if (nEndCol < nBottomRightTileCol)
        nEndCol = nBottomRightTileCol;

    if (nEndRow < nBottomRightTileRow)
        nEndRow = nBottomRightTileRow;

    nTopLeftTileCol = std::max<sal_Int32>(nTopLeftTileCol, 0);
    nTopLeftTileRow = std::max<sal_Int32>(nTopLeftTileRow, 0);
    nTopLeftTileColOrigin = o3tl::convert(nTopLeftTileColOrigin, o3tl::Length::px, o3tl::Length::twip);
    nTopLeftTileRowOrigin = o3tl::convert(nTopLeftTileRowOrigin, o3tl::Length::px, o3tl::Length::twip);

    // Checkout -> 'rDoc.ExtendMerge' ... if we miss merged cells.

    // Origin must be the offset of the first col and row
    // containing our top-left pixel.
    const MapMode aOriginalMode = rDevice.GetMapMode();
    MapMode aAbsMode = aOriginalMode;
    const Point aOrigin(-nTopLeftTileColOrigin, -nTopLeftTileRowOrigin);
    aAbsMode.SetOrigin(aOrigin);
    rDevice.SetMapMode(aAbsMode);

    ScTableInfo aTabInfo(nTopLeftTileRow, nBottomRightTileRow, false);
    rDoc.FillInfo(aTabInfo, nTopLeftTileCol, nTopLeftTileRow,
                   nBottomRightTileCol, nBottomRightTileRow,
                   nTab, fPPTX, fPPTY, falsefalse);

// FIXME: is this called some
//        Point aScrPos = mrViewData.GetScrPos( nX1, nY1, eWhich );

    ScOutputData aOutputData(&rDevice, OUTTYPE_WINDOW, aTabInfo, &rDoc, nTab,
                             -nTopLeftTileColOffset,
                             -nTopLeftTileRowOffset,
                             nTopLeftTileCol, nTopLeftTileRow,
                             nBottomRightTileCol, nBottomRightTileRow,
                             fPPTX, fPPTY, nullptr, nullptr);

    // setup the SdrPage so that drawinglayer works correctly
    ScDrawLayer* pModel = rDoc.GetDrawLayer();
    if (pModel)
    {
        bool bPrintTwipsMsgs = comphelper::LibreOfficeKit::isCompatFlagSet(
                comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs);
        if (!mpLOKDrawView)
        {
            mpLOKDrawView.reset(bPrintTwipsMsgs ?
                new ScLOKDrawView(
                    &rDevice,
                    mrViewData) :
                new FmFormView(
                    *pModel,
                    &rDevice));
        }

        mpLOKDrawView->SetNegativeX(bLayoutRTL);
        mpLOKDrawView->ShowSdrPage(mpLOKDrawView->GetModel().GetPage(nTab));
        aOutputData.SetDrawView(mpLOKDrawView.get());
        aOutputData.SetSpellCheckContext(mpSpellCheckCxt.get());
    }

    // draw the content
    DrawContent(rDevice, aTabInfo, aOutputData, true);
    rDevice.SetMapMode(aOriginalMode);

    // Paint the chart(s) in edit mode.
    LokChartHelper::PaintAllChartsOnTile(rDevice, nOutputWidth, nOutputHeight,
        nTilePosX, nTilePosY, nTileWidth, nTileHeight, bLayoutRTL);

    rDevice.SetMapMode(aOriginalMode);

    // Flag drawn formula cells "unchanged".
    rDoc.ResetChanged(ScRange(nTopLeftTileCol, nTopLeftTileRow, nTab, nBottomRightTileCol, nBottomRightTileRow, nTab));
    rDoc.PrepareFormulaCalc();

    mrViewData.SetZoom(origZoomX, origZoomY, true);
    if (bChangeZoom)
    {
        if (ScDrawView* pDrawView = mrViewData.GetScDrawView())
            pDrawView->resetGridOffsetsForAllSdrPageViews();
    }

    if (bLayoutRTL)
    {
        Bitmap aCellBMP = rDevice.GetBitmap(Point(0, 0), Size(nOutputWidth, nOutputHeight));
        aCellBMP.Mirror(BmpMirrorFlags::Horizontal);
        rDevice.DrawBitmap(Point(0, 0), Size(nOutputWidth, nOutputHeight), aCellBMP);
    }
}

void ScGridWindow::LogicInvalidatePart(const tools::Rectangle* pRectangle, int nPart)
{
    tools::Rectangle aRectangle;
    tools::Rectangle* pResultRectangle;
    if (!pRectangle)
        pResultRectangle = nullptr;
    else
    {
        aRectangle = *pRectangle;
        // When dragging shapes the map mode is disabled.
        if (IsMapModeEnabled())
        {
            if (GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
            {
                aRectangle = o3tl::convert(aRectangle, o3tl::Length::mm100, o3tl::Length::twip);
            }
        }
        else
            aRectangle = PixelToLogic(aRectangle, MapMode(MapUnit::MapTwip));
        pResultRectangle = &aRectangle;
    }

    // Trim invalidation rectangle overlapping negative X region in RTL mode.
    if (pResultRectangle && pResultRectangle->Left() < 0
        && mrViewData.GetDocument().IsLayoutRTL(mrViewData.GetTabNo()))
    {
        pResultRectangle->SetLeft(0);
        if (pResultRectangle->Right() < 0)
            pResultRectangle->SetRight(0);
    }

    ScTabViewShell* pViewShell = mrViewData.GetViewShell();
    SfxLokHelper::notifyInvalidation(pViewShell, nPart, pResultRectangle);
}

void ScGridWindow::LogicInvalidate(const tools::Rectangle* pRectangle)
{
    ScTabViewShell* pViewShell = mrViewData.GetViewShell();
    LogicInvalidatePart(pRectangle, pViewShell->getPart());
}

bool ScGridWindow::InvalidateByForeignEditView(EditView* pEditView)
{
    if (!pEditView)
        return false;

    auto* pGridWin = dynamic_cast<ScGridWindow*>(pEditView->GetWindow());
    if (!pGridWin)
        return false;

    const ScViewData& rViewData = pGridWin->getViewData();
    tools::Long nRefTabNo = rViewData.GetRefTabNo();
    tools::Long nX = rViewData.GetCurXForTab(nRefTabNo);
    tools::Long nY = rViewData.GetCurYForTab(nRefTabNo);

    tools::Rectangle aPixRect = getViewData().GetEditArea(eWhich, nX, nY, this, nullptr, true);
    tools::Rectangle aLogicRect = PixelToLogic(aPixRect, getViewData().GetLogicMode());
    Invalidate(pEditView->IsNegativeX() ? lcl_negateRectX(aLogicRect) : aLogicRect);

    return true;
}

void ScGridWindow::SetCellSelectionPixel(int nType, int nPixelX, int nPixelY)
{
    ScTabView* pTabView = mrViewData.GetView();
    ScTabViewShell* pViewShell = mrViewData.GetViewShell();
--> --------------------

--> maximum size reached

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

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

¤ Dauer der Verarbeitung: 0.20 Sekunden  ¤

*© 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.