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

Quelle  ruler.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 <tools/debug.hxx>
#include <tools/poly.hxx>
#include <vcl/event.hxx>
#include <vcl/themecolors.hxx>
#include <vcl/settings.hxx>
#include <vcl/vcllayout.hxx>
#include <vcl/virdev.hxx>
#include <vcl/ptrstyle.hxx>
#include <sal/log.hxx>

#include <svtools/ruler.hxx>
#include <svtools/svtresid.hxx>
#include <svtools/strings.hrc>
#include <svtools/colorcfg.hxx>
#include "accessibleruler.hxx"

#include <memory>
#include <vector>

using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::accessibility;

#define RULER_OFF           3
#define RULER_RESIZE_OFF    4
#define RULER_MIN_SIZE      3

#define RULER_VAR_SIZE      8

#define RULER_UPDATE_LINES  0x01

#define RULER_CLIP          150

#define RULER_UNIT_MM       0
#define RULER_UNIT_CM       1
#define RULER_UNIT_M        2
#define RULER_UNIT_KM       3
#define RULER_UNIT_INCH     4
#define RULER_UNIT_FOOT     5
#define RULER_UNIT_MILE     6
#define RULER_UNIT_POINT    7
#define RULER_UNIT_PICA     8
#define RULER_UNIT_CHAR     9
#define RULER_UNIT_LINE    10
#define RULER_UNIT_COUNT   11

namespace
{
/**
 * Pre-calculates glyph items for rText on rRenderContext. Subsequent calls
 * avoid the calculation and just return a pointer to rTextGlyphs.
 */

SalLayoutGlyphs* lcl_GetRulerTextGlyphs(const vcl::RenderContext& rRenderContext, const OUString& rText,
                                        SalLayoutGlyphs& rTextGlyphs)
{
    if (rTextGlyphs.IsValid())
        // Use pre-calculated result.
        return &rTextGlyphs;

    // Calculate glyph items.

    std::unique_ptr<SalLayout> pLayout = rRenderContext.ImplLayout(
        rText, 0, rText.getLength(), Point(0, 0), 0, {}, {}, SalLayoutFlags::GlyphItemsOnly);
    if (!pLayout)
        return nullptr;

    // Remember the calculation result.
    rTextGlyphs = pLayout->GetGlyphs();

    return &rTextGlyphs;
}
}

class ImplRulerData
{
    friend class Ruler;

private:
    std::vector<RulerLine>    pLines;
    std::vector<RulerBorder>  pBorders;
    std::vector<RulerIndent>  pIndents;
    std::vector<RulerTab>     pTabs;

    tools::Long       nNullVirOff;
    tools::Long       nRulVirOff;
    tools::Long       nRulWidth;
    tools::Long       nPageOff;
    tools::Long       nPageWidth;
    tools::Long       nNullOff;
    tools::Long       nMargin1;
    tools::Long       nMargin2;
    // In this context, "frame margin" means paragraph margins (indents)
    tools::Long       nLeftFrameMargin;
    tools::Long       nRightFrameMargin;
    RulerMarginStyle nMargin1Style;
    RulerMarginStyle nMargin2Style;
    bool       bAutoPageWidth;
    bool       bTextRTL;

public:
    ImplRulerData();
};

ImplRulerData::ImplRulerData() :
    nNullVirOff       (0),
    nRulVirOff        (0),
    nRulWidth         (0),
    nPageOff          (0),
    nPageWidth        (0),
    nNullOff          (0),
    nMargin1          (0),
    nMargin2          (0),
    nLeftFrameMargin  (0),
    nRightFrameMargin (0),
    nMargin1Style     (RulerMarginStyle::NONE),
    nMargin2Style     (RulerMarginStyle::NONE),
    bAutoPageWidth    (true), // Page width == EditWin width
    bTextRTL          (false)
{
}

const RulerUnitData aImplRulerUnitTab[RULER_UNIT_COUNT] =
{
{ MapUnit::Map100thMM,        100,    25.0,    25.0,     50.0,    100.0,  " mm"    }, // MM
{ MapUnit::Map100thMM,       1000,   100.0,   500.0,   1000.0,   1000.0,  " cm"    }, // CM
{ MapUnit::MapMM,             1000,    10.0,   250.0,    500.0,   1000.0,  " m"     }, // M
{ MapUnit::MapCM,           100000, 12500.0, 25000.0,  50000.0, 100000.0,  " km"    }, // KM
{ MapUnit::Map1000thInch,    1000,    62.5,   125.0,    500.0,   1000.0,  "\"" }, // INCH
{ MapUnit::Map100thInch,     1200,   120.0,   120.0,    600.0,   1200.0,  "'"      }, // FOOT
{ MapUnit::Map10thInch,    633600, 63360.0, 63360.0, 316800.0, 633600.0,  " miles" }, // MILE
{ MapUnit::MapPoint,             1,    12.0,    12.0,     12.0,     36.0,  " pt"    }, // POINT
{ MapUnit::Map100thMM,        423,   423.0,   423.0,    423.0,    846.0,  " pc"    }, // PICA
{ MapUnit::Map100thMM,        371,   371.0,   371.0,    371.0,    743.0,  " ch"    }, // CHAR
{ MapUnit::Map100thMM,        551,   551.0,   551.0,    551.0,   1102.0,  " li"    }  // LINE
};

static RulerTabData ruler_tab =
{
    0, // DPIScaleFactor to be set
    7, // ruler_tab_width
    6, // ruler_tab_height
    2, // ruler_tab_height2
    2, // ruler_tab_width2
    8, // ruler_tab_cwidth
    4, // ruler_tab_cwidth2
    4, // ruler_tab_cwidth3
    2, // ruler_tab_cwidth4
    4, // ruler_tab_dheight
    1, // ruler_tab_dheight2
    5, // ruler_tab_dwidth
    3, // ruler_tab_dwidth2
    3, // ruler_tab_dwidth3
    1, // ruler_tab_dwidth4
    5  // ruler_tab_textoff
};

void Ruler::ImplInit( WinBits nWinBits )
{
    // Set default WinBits
    if ( !(nWinBits & WB_VERT) )
    {
        nWinBits |= WB_HORZ;

        // RTL: no UI mirroring for horizontal rulers, because
        // the document is also not mirrored
        EnableRTL( false );
    }

    // Initialize variables
    mnWinStyle      = nWinBits;             // Window-Style
    mnBorderOff     = 0;                    // Border-Offset
    mnWinOff        = 0;                    // EditWinOffset
    mnWinWidth      = 0;                    // EditWinWidth
    mnWidth         = 0;                    // Window width
    mnHeight        = 0;                    // Window height
    mnVirOff        = 0;                    // Offset of VirtualDevice from top-left corner
    mnVirWidth      = 0;                    // width or height from VirtualDevice
    mnVirHeight     = 0;                    // height of width from VirtualDevice
    mnDragPos       = 0;                    // Drag-Position (Null point)
    mnDragAryPos    = 0;                    // Drag-Array-Index
    mnDragSize      = RulerDragSize::Move;  // Did size change at dragging
    mnDragModifier  = 0;                    // Modifier key at dragging
    mnExtraStyle    = 0;                    // Style of Extra field
    mnCharWidth     = 371;
    mnLineHeight    = 551;
    mbCalc          = true;                 // Should recalculate page width
    mbFormat        = true;                 // Should redraw
    mbDrag          = false;                // Currently at dragging
    mbDragDelete    = false;                // Has mouse left the dragging area
    mbDragCanceled  = false;                // Dragging cancelled?
    mbAutoWinWidth  = true;                 // EditWinWidth == RulerWidth
    mbActive        = true;                 // Is ruler active
    mnUpdateFlags   = 0;                    // What needs to be updated
    mpData          = mpSaveData.get();     // Pointer to normal data
    meExtraType     = RulerExtra::DontKnow; // What is in extra field
    meDragType      = RulerType::DontKnow;  // Which element is dragged

    // Initialize Units
    mnUnitIndex     = RULER_UNIT_CM;
    meUnit          = FieldUnit::CM;
    maZoom          = Fraction( 1, 1 );

    // Recalculate border widths
    if ( nWinBits & WB_BORDER )
        mnBorderWidth = 1;
    else
        mnBorderWidth = 0;

    // Settings
    ImplInitSettings( truetruetrue );

    // Setup the default size
    tools::Rectangle aRect;
    GetOutDev()->GetTextBoundRect( aRect, u"0123456789"_ustr );
    tools::Long nDefHeight = aRect.GetHeight() + RULER_OFF * 2 + ruler_tab.textoff * 2 + mnBorderWidth;

    Size aDefSize;
    if ( nWinBits & WB_HORZ )
        aDefSize.setHeight( nDefHeight );
    else
        aDefSize.setWidth( nDefHeight );
    SetOutputSizePixel( aDefSize );
    SetType(WindowType::RULER);
}

Ruler::Ruler( vcl::Window* pParent, WinBits nWinStyle ) :
    Window( pParent, nWinStyle & WB_3DLOOK ),
    maVirDev( VclPtr<VirtualDevice>::Create(*GetOutDev()) ),
    maMapMode( MapUnit::Map100thMM ),
    mpSaveData(new ImplRulerData),
    mpData(nullptr),
    mpDragData(new ImplRulerData)
{
    // Check to see if the ruler constructor has
    // already been called before otherwise
    // we end up with over-scaled elements
    if (ruler_tab.DPIScaleFactor == 0)
    {
        ruler_tab.DPIScaleFactor = GetDPIScaleFactor();
        ruler_tab.width    *= ruler_tab.DPIScaleFactor;
        ruler_tab.height   *= ruler_tab.DPIScaleFactor;
        ruler_tab.height2  *= ruler_tab.DPIScaleFactor;
        ruler_tab.width2   *= ruler_tab.DPIScaleFactor;
        ruler_tab.cwidth   *= ruler_tab.DPIScaleFactor;
        ruler_tab.cwidth2  *= ruler_tab.DPIScaleFactor;
        ruler_tab.cwidth3  *= ruler_tab.DPIScaleFactor;
        ruler_tab.cwidth4  *= ruler_tab.DPIScaleFactor;
        ruler_tab.dheight  *= ruler_tab.DPIScaleFactor;
        ruler_tab.dheight2 *= ruler_tab.DPIScaleFactor;
        ruler_tab.dwidth   *= ruler_tab.DPIScaleFactor;
        ruler_tab.dwidth2  *= ruler_tab.DPIScaleFactor;
        ruler_tab.dwidth3  *= ruler_tab.DPIScaleFactor;
        ruler_tab.dwidth4  *= ruler_tab.DPIScaleFactor;
        ruler_tab.textoff  *= ruler_tab.DPIScaleFactor;
    }


    ImplInit( nWinStyle );
}

Ruler::~Ruler()
{
    disposeOnce();
}

void Ruler::dispose()
{
    mpSaveData.reset();
    mpDragData.reset();
    Window::dispose();
}

void Ruler::ImplVDrawLine(vcl::RenderContext& rRenderContext, tools::Long nX1, tools::Long nY1, tools::Long nX2, tools::Long nY2)
{
    if ( nX1 < -RULER_CLIP )
    {
        nX1 = -RULER_CLIP;
        if ( nX2 < -RULER_CLIP )
            return;
    }
    tools::Long nClip = mnVirWidth + RULER_CLIP;
    if ( nX2 > nClip )
    {
        nX2 = nClip;
        if ( nX1 > nClip )
            return;
    }

    if ( mnWinStyle & WB_HORZ )
        rRenderContext.DrawLine( Point( nX1, nY1 ), Point( nX2, nY2 ) );
    else
        rRenderContext.DrawLine( Point( nY1, nX1 ), Point( nY2, nX2 ) );
}

void Ruler::ImplVDrawRect(vcl::RenderContext& rRenderContext, tools::Long nX1, tools::Long nY1, tools::Long nX2, tools::Long nY2)
{
    if ( nX1 < -RULER_CLIP )
    {
        nX1 = -RULER_CLIP;
        if ( nX2 < -RULER_CLIP )
            return;
    }
    tools::Long nClip = mnVirWidth + RULER_CLIP;
    if ( nX2 > nClip )
    {
        nX2 = nClip;
        if ( nX1 > nClip )
            return;
    }

    if ( mnWinStyle & WB_HORZ )
        rRenderContext.DrawRect(tools::Rectangle(nX1, nY1, nX2, nY2));
    else
        rRenderContext.DrawRect(tools::Rectangle(nY1, nX1, nY2, nX2));
}

void Ruler::ImplVDrawText(vcl::RenderContext& rRenderContext, tools::Long nX, tools::Long nY, const OUString& rText, tools::Long nMin, tools::Long nMax)
{
    tools::Rectangle aRect;
    SalLayoutGlyphs* pTextLayout
        = lcl_GetRulerTextGlyphs(rRenderContext, rText, maTextGlyphs[rText]);
    rRenderContext.GetTextBoundRect(aRect, rText, 0, 0, -1, 0, {}, {}, pTextLayout);

    tools::Long nShiftX = ( aRect.GetWidth() / 2 ) + aRect.Left();
    tools::Long nShiftY = ( aRect.GetHeight() / 2 ) + aRect.Top();

    if ( (nX > -RULER_CLIP) && (nX < mnVirWidth + RULER_CLIP) && ( nX < nMax - nShiftX ) && ( nX > nMin + nShiftX ) )
    {
        if ( mnWinStyle & WB_HORZ )
            rRenderContext.DrawText(Point(nX - nShiftX, nY - nShiftY), rText, 0, -1, nullptr,
                                    nullptr, pTextLayout);
        else
            rRenderContext.DrawText(Point(nY - nShiftX, nX - nShiftY), rText, 0, -1, nullptr,
                                    nullptr, pTextLayout);
    }
}

void Ruler::ImplInvertLines(vcl::RenderContext& rRenderContext)
{
    // Position lines
    if (mpData->pLines.empty() || !mbActive || mbDrag || mbFormat || (mnUpdateFlags & RULER_UPDATE_LINES) )
        return;

    tools::Long nNullWinOff = mpData->nNullVirOff + mnVirOff;
    tools::Long nRulX1      = mpData->nRulVirOff  + mnVirOff;
    tools::Long nRulX2      = nRulX1 + mpData->nRulWidth;
    tools::Long nY          = (RULER_OFF * 2) + mnVirHeight - 1;

    // Calculate rectangle
    tools::Rectangle aRect;
    if (mnWinStyle & WB_HORZ)
        aRect.SetBottom( nY );
    else
        aRect.SetRight( nY );

    // Draw lines
    for (const RulerLine & rLine : mpData->pLines)
    {
        const tools::Long n = rLine.nPos + nNullWinOff;
        if ((n >= nRulX1) && (n < nRulX2))
        {
            if (mnWinStyle & WB_HORZ )
            {
                aRect.SetLeft( n );
                aRect.SetRight( n );
            }
            else
            {
                aRect.SetTop( n );
                aRect.SetBottom( n );
            }
            tools::Rectangle aTempRect = aRect;

            if (mnWinStyle & WB_HORZ)
                aTempRect.SetBottom( RULER_OFF - 1 );
            else
                aTempRect.SetRight( RULER_OFF - 1 );

            rRenderContext.Erase(aTempRect);

            if (mnWinStyle & WB_HORZ)
            {
                aTempRect.SetBottom( aRect.Bottom() );
                aTempRect.SetTop( aTempRect.Bottom() - RULER_OFF + 1 );
            }
            else
            {
                aTempRect.SetRight( aRect.Right() );
                aTempRect.SetLeft( aTempRect.Right() - RULER_OFF + 1 );
            }
            rRenderContext.Erase(aTempRect);
            GetOutDev()->Invert(aRect);
        }
    }
    mnUpdateFlags = 0;
}

void Ruler::ImplDrawTicks(vcl::RenderContext& rRenderContext, tools::Long nMin, tools::Long nMax, tools::Long nStart, tools::Long nTop, tools::Long nBottom)
{
    const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
    double nCenter = nTop + ((nBottom - nTop) / 2);

    tools::Long nTickLength3 = (nBottom - nTop) * 0.5;
    tools::Long nTickLength2 = nTickLength3 * 0.66;
    tools::Long nTickLength1 = nTickLength2 * 0.66;

    tools::Long nScale = ruler_tab.DPIScaleFactor;
    tools::Long DPIOffset = nScale - 1;

    double nTick4 = aImplRulerUnitTab[mnUnitIndex].nTick4;
    double nTick2 = 0;
    double nTickCount = aImplRulerUnitTab[mnUnitIndex].nTick1 / nScale;
    double nTickUnit = 0;
    tools::Long nTickWidth;
    bool bNoTicks = false;

    Size aPixSize = rRenderContext.LogicToPixel(Size(nTick4, nTick4), maMapMode);

    if (mnUnitIndex == RULER_UNIT_CHAR)
    {
        if (mnCharWidth == 0)
            mnCharWidth = 371;
        nTick4 = mnCharWidth * 2;
        nTick2 = mnCharWidth;
        nTickCount = mnCharWidth;
        nTickUnit = mnCharWidth;
    }
    else if (mnUnitIndex == RULER_UNIT_LINE)
    {
        if (mnLineHeight == 0)
            mnLineHeight = 551;
        nTick4 = mnLineHeight * 2;
        nTick2 = mnLineHeight;
        nTickUnit = mnLineHeight;
        nTickCount = mnLineHeight;
    }

    if (mnWinStyle & WB_HORZ)
    {
        nTickWidth = aPixSize.Width();
    }
    else
    {
        vcl::Font aFont = rRenderContext.GetFont();
        if (mnWinStyle & WB_RIGHT_ALIGNED)
            aFont.SetOrientation(2700_deg10);
        else
            aFont.SetOrientation(900_deg10);
        rRenderContext.SetFont(aFont);
        nTickWidth = aPixSize.Height();
    }

    tools::Long nMaxWidth = rRenderContext.PixelToLogic(Size(mpData->nPageWidth, 0), maMapMode).Width();
    if (nMaxWidth < 0)
        nMaxWidth = -nMaxWidth;

    if ((mnUnitIndex == RULER_UNIT_CHAR) || (mnUnitIndex == RULER_UNIT_LINE))
        nMaxWidth /= nTickUnit;
    else
        nMaxWidth /= aImplRulerUnitTab[mnUnitIndex].nTickUnit;

    OUString aNumString = OUString::number(nMaxWidth);
    tools::Long nTxtWidth = rRenderContext.GetTextWidth( aNumString );
    const tools::Long nTextOff = 4;

    // Determine the number divider for ruler drawn numbers - means which numbers
    // should be shown on the ruler and which should be skipped because the ruler
    // is not big enough to draw them
    if (nTickWidth < nTxtWidth + nTextOff)
    {
        // Calculate the scale of the ruler
        tools::Long nMulti = 1;
        tools::Long nOrgTick4 = nTick4;

        while (nTickWidth < nTxtWidth + nTextOff)
        {
            tools::Long nOldMulti = nMulti;
            if (nTickWidth == 0)
                nMulti *= 10;
            else if (nMulti < 10)
                nMulti++;
            else if (nMulti < 100)
                nMulti += 10;
            else if (nMulti < 1000)
                nMulti += 100;
            else
                nMulti += 1000;

            // Overflow - in this case don't draw ticks and exit
            if (nMulti < nOldMulti)
            {
                bNoTicks = true;
                break;
            }

            nTick4 = nOrgTick4 * nMulti;
            aPixSize = rRenderContext.LogicToPixel(Size(nTick4, nTick4), maMapMode);
            if (mnWinStyle & WB_HORZ)
                nTickWidth = aPixSize.Width();
            else
                nTickWidth = aPixSize.Height();
        }
        nTickCount = nTick4;
    }
    else
    {
        rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
    }

    if (bNoTicks)
        return;

    tools::Long n = 0;
    double nTick = 0.0;
    double nTick3 = 0;

    if ((mnUnitIndex != RULER_UNIT_CHAR) && (mnUnitIndex != RULER_UNIT_LINE))
    {
        nTick2 = aImplRulerUnitTab[mnUnitIndex].nTick2;
        nTick3 = aImplRulerUnitTab[mnUnitIndex].nTick3;
    }

    Size nTickGapSize;

    nTickGapSize = rRenderContext.LogicToPixel(Size(nTickCount, nTickCount), maMapMode);
    tools::Long nTickGap1 = mnWinStyle & WB_HORZ ? nTickGapSize.Width() : nTickGapSize.Height();
    nTickGapSize = rRenderContext.LogicToPixel(Size(nTick2, nTick2), maMapMode);
    tools::Long nTickGap2 = mnWinStyle & WB_HORZ ? nTickGapSize.Width() : nTickGapSize.Height();
    nTickGapSize = rRenderContext.LogicToPixel(Size(nTick3, nTick3), maMapMode);
    tools::Long nTickGap3 = mnWinStyle & WB_HORZ ? nTickGapSize.Width() : nTickGapSize.Height();

    while (((nStart - n) >= nMin) || ((nStart + n) <= nMax))
    {
        // Null point
        if (nTick == 0.0)
        {
            if (nStart > nMin)
            {
                // 0 is only painted when Margin1 is not equal to zero
                if ((mpData->nMargin1Style & RulerMarginStyle::Invisible) || (mpData->nMargin1 != 0))
                {
                    aNumString = "0";
                    ImplVDrawText(rRenderContext, nStart, nCenter, aNumString);
                }
            }
        }
        else
        {
            aPixSize = rRenderContext.LogicToPixel(Size(nTick, nTick), maMapMode);

            if (mnWinStyle & WB_HORZ)
                n = aPixSize.Width();
            else
                n = aPixSize.Height();

            // Tick4 - Output (Text)
            double aStep = nTick / nTick4;
            double aRest = std::abs(aStep - std::floor(aStep));
            double nAcceptanceDelta = 0.0001;
            rRenderContext.SetFillColor(rStyleSettings.GetShadowColor());

            if (aRest < nAcceptanceDelta)
            {
                if ((mnUnitIndex == RULER_UNIT_CHAR) || (mnUnitIndex == RULER_UNIT_LINE))
                    aNumString = OUString::number(nTick / nTickUnit);
                else
                    aNumString = OUString::number(nTick / aImplRulerUnitTab[mnUnitIndex].nTickUnit);

                tools::Long nHorizontalLocation = nStart + n;
                ImplVDrawText(rRenderContext, nHorizontalLocation, nCenter, aNumString, nMin, nMax);

                if (nMin < nHorizontalLocation && nHorizontalLocation < nMax)
                {
                    ImplVDrawRect(rRenderContext, nHorizontalLocation, nBottom - 1 * nScale, nHorizontalLocation + DPIOffset, nBottom);
                    ImplVDrawRect(rRenderContext, nHorizontalLocation, nTop, nHorizontalLocation + DPIOffset, nTop + 1 * nScale);
                }

                nHorizontalLocation = nStart - n;
                ImplVDrawText(rRenderContext, nHorizontalLocation, nCenter, aNumString, nMin, nMax);

                if (nMin < nHorizontalLocation && nHorizontalLocation < nMax)
                {
                    ImplVDrawRect(rRenderContext, nHorizontalLocation, nBottom,
                                                  nHorizontalLocation + DPIOffset, nBottom - 1 * nScale);
                    ImplVDrawRect(rRenderContext, nHorizontalLocation, nTop,
                                                  nHorizontalLocation + DPIOffset, nTop + 1 * nScale);
                }
            }
            // Tick/Tick2 - Output (Strokes)
            else
            {
                tools::Long nTickLength = nTickLength1;

                aStep = (nTick / nTick2);
                aRest = std::abs(aStep - std::floor(aStep));
                if (aRest < nAcceptanceDelta)
                    nTickLength = nTickLength2;

                aStep = (nTick / nTick3);
                aRest = std::abs(aStep - std::floor(aStep));
                if (aRest < nAcceptanceDelta )
                    nTickLength = nTickLength3;

                if ((nTickLength == nTickLength1 && nTickGap1 > 6) ||
                    (nTickLength == nTickLength2 && nTickGap2 > 6) ||
                    (nTickLength == nTickLength3 && nTickGap3 > 6))
                {
                    tools::Long nT1 = nCenter - (nTickLength / 2.0);
                    tools::Long nT2 = nT1 + nTickLength - 1;
                    tools::Long nT;

                    nT = nStart + n;

                    if (nT < nMax)
                        ImplVDrawRect(rRenderContext, nT, nT1, nT + DPIOffset, nT2);
                    nT = nStart - n;
                    if (nT > nMin)
                        ImplVDrawRect(rRenderContext, nT, nT1, nT + DPIOffset, nT2);
                }
            }
        }
        nTick += nTickCount;
    }
}

void Ruler::ImplDrawBorders(vcl::RenderContext& rRenderContext, tools::Long nMin, tools::Long nMax, tools::Long nVirTop, tools::Long nVirBottom)
{
    const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
    tools::Long    n;
    tools::Long    n1;
    tools::Long    n2;
    tools::Long    nTemp1;
    tools::Long    nTemp2;

    for (std::vector<RulerBorder>::size_type i = 0; i < mpData->pBorders.size(); i++)
    {
        if (mpData->pBorders[i].nStyle & RulerBorderStyle::Invisible)
            continue;

        n1 = mpData->pBorders[i].nPos + mpData->nNullVirOff;
        n2 = n1 + mpData->pBorders[i].nWidth;

        if (((n1 >= nMin) && (n1 <= nMax)) || ((n2 >= nMin) && (n2 <= nMax)))
        {
            if ((n2 - n1) > 3)
            {
                rRenderContext.SetLineColor();
                rRenderContext.SetFillColor(rStyleSettings.GetFaceColor());
                ImplVDrawRect(rRenderContext, n1, nVirTop, n2, nVirBottom);

                rRenderContext.SetLineColor(rStyleSettings.GetLightColor());
                ImplVDrawLine(rRenderContext, n1 + 1, nVirTop, n1 + 1, nVirBottom);
                ImplVDrawLine(rRenderContext, n1,     nVirTop, n2,     nVirTop);

                rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
                ImplVDrawLine(rRenderContext, n1,     nVirTop,    n1,     nVirBottom);
                ImplVDrawLine(rRenderContext, n1,     nVirBottom, n2,     nVirBottom);
                ImplVDrawLine(rRenderContext, n2 - 1, nVirTop,    n2 - 1, nVirBottom);

                rRenderContext.SetLineColor(rStyleSettings.GetDarkShadowColor());
                ImplVDrawLine(rRenderContext, n2, nVirTop, n2, nVirBottom);

                if (mpData->pBorders[i].nStyle & RulerBorderStyle::Variable)
                {
                    if (n2 - n1 > RULER_VAR_SIZE + 4)
                    {
                        nTemp1 = n1 + (((n2 - n1 + 1) - RULER_VAR_SIZE) / 2);
                        nTemp2 = nVirTop + (((nVirBottom - nVirTop + 1) - RULER_VAR_SIZE) / 2);
                        tools::Long nTemp3 = nTemp1 + RULER_VAR_SIZE - 1;
                        tools::Long nTemp4 = nTemp2 + RULER_VAR_SIZE - 1;
                        tools::Long nTempY = nTemp2;

                        rRenderContext.SetLineColor(rStyleSettings.GetLightColor());
                        while (nTempY <= nTemp4)
                        {
                            ImplVDrawLine(rRenderContext, nTemp1, nTempY, nTemp3, nTempY);
                            nTempY += 2;
                        }

                        nTempY = nTemp2 + 1;
                        rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
                        while (nTempY <= nTemp4)
                        {
                            ImplVDrawLine(rRenderContext, nTemp1, nTempY, nTemp3, nTempY);
                            nTempY += 2;
                        }
                    }
                }

                if (mpData->pBorders[i].nStyle & RulerBorderStyle::Sizeable)
                {
                    if (n2 - n1 > RULER_VAR_SIZE + 10)
                    {
                        rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
                        ImplVDrawLine(rRenderContext, n1 + 4, nVirTop + 3, n1 + 4, nVirBottom - 3);
                        ImplVDrawLine(rRenderContext, n2 - 5, nVirTop + 3, n2 - 5, nVirBottom - 3);
                        rRenderContext.SetLineColor(rStyleSettings.GetLightColor());
                        ImplVDrawLine(rRenderContext, n1 + 5, nVirTop + 3, n1 + 5, nVirBottom - 3);
                        ImplVDrawLine(rRenderContext, n2 - 4, nVirTop + 3, n2 - 4, nVirBottom - 3);
                    }
                }
            }
            else
            {
                n = n1 + ((n2 - n1) / 2);
                rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());

                ImplVDrawLine(rRenderContext, n - 1, nVirTop, n - 1, nVirBottom);
                ImplVDrawLine(rRenderContext, n + 1, nVirTop, n + 1, nVirBottom);
                rRenderContext.SetLineColor();
                rRenderContext.SetFillColor(rStyleSettings.GetWindowColor());
                ImplVDrawRect(rRenderContext, n, nVirTop, n, nVirBottom);
            }
        }
    }
}

void Ruler::ImplDrawIndent(vcl::RenderContext& rRenderContext, const tools::Polygon& rPoly, bool bIsHit)
{
    const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();

    rRenderContext.SetLineColor(rStyleSettings.GetDarkShadowColor());
    rRenderContext.SetFillColor(bIsHit ? rStyleSettings.GetDarkShadowColor() : rStyleSettings.GetWorkspaceColor());
    tools::Polygon aPolygon(rPoly);
    aPolygon.Optimize(PolyOptimizeFlags::CLOSE);
    rRenderContext.DrawPolygon(aPolygon);
}

void Ruler::ImplDrawIndents(vcl::RenderContext& rRenderContext, tools::Long nMin, tools::Long nMax, tools::Long nVirTop, tools::Long nVirBottom)
{
    tools::Long n;
    tools::Long nIndentHeight = (mnVirHeight / 2) - 1;
    tools::Long nIndentWidth2 = nIndentHeight-3;

    tools::Polygon aPoly(5);

    for (std::vector<RulerIndent>::size_type j = 0; j < mpData->pIndents.size(); j++)
    {
        if (mpData->pIndents[j].bInvisible)
            continue;

        RulerIndentStyle nIndentStyle = mpData->pIndents[j].nStyle;

        n = mpData->pIndents[j].nPos+mpData->nNullVirOff;

        if ((n >= nMin) && (n <= nMax))
        {
            if (nIndentStyle == RulerIndentStyle::Bottom)
            {
                aPoly.SetPoint(Point(n + 0, nVirBottom - nIndentHeight), 0);
                aPoly.SetPoint(Point(n - nIndentWidth2, nVirBottom - 3), 1);
                aPoly.SetPoint(Point(n - nIndentWidth2, nVirBottom),     2);
                aPoly.SetPoint(Point(n + nIndentWidth2, nVirBottom),     3);
                aPoly.SetPoint(Point(n + nIndentWidth2, nVirBottom - 3), 4);
            }
            else
            {
                aPoly.SetPoint(Point(n + 0, nVirTop + nIndentHeight), 0);
                aPoly.SetPoint(Point(n - nIndentWidth2, nVirTop + 3), 1);
                aPoly.SetPoint(Point(n - nIndentWidth2, nVirTop),     2);
                aPoly.SetPoint(Point(n + nIndentWidth2, nVirTop),     3);
                aPoly.SetPoint(Point(n + nIndentWidth2, nVirTop + 3), 4);
            }

            if (0 == (mnWinStyle & WB_HORZ))
            {
                Point aTmp;
                for (sal_uInt16 i = 0; i < 5; i++)
                {
                    aTmp = aPoly[i];
                    Point aSet(nVirBottom - aTmp.Y(), aTmp.X());
                    aPoly[i] = aSet;
                }
            }
            bool bIsHit = false;
            if (mxCurrentHitTest != nullptr && mxCurrentHitTest->eType == RulerType::Indent)
            {
                bIsHit = mxCurrentHitTest->nAryPos == j;
            }
            else if(mbDrag && meDragType == RulerType::Indent)
            {
                bIsHit = mnDragAryPos == j;
            }
            ImplDrawIndent(rRenderContext, aPoly, bIsHit);
        }
    }
}

static void ImplCenterTabPos(Point& rPos, sal_uInt16 nTabStyle)
{
    bool bRTL  = 0 != (nTabStyle & RULER_TAB_RTL);
    nTabStyle &= RULER_TAB_STYLE;
    rPos.AdjustY(ruler_tab.height/2 );

    if ( (!bRTL && nTabStyle == RULER_TAB_LEFT) ||
         ( bRTL && nTabStyle == RULER_TAB_RIGHT) )
    {
        rPos.AdjustX( -(ruler_tab.width / 2) );
    }
    else if ( (!bRTL && nTabStyle == RULER_TAB_RIGHT) ||
              ( bRTL && nTabStyle == RULER_TAB_LEFT) )
    {
        rPos.AdjustX(ruler_tab.width / 2 );
    }
}

static void lcl_RotateRect_Impl(tools::Rectangle& rRect, const tools::Long nReference, bool bRightAligned)
{
    if (rRect.IsEmpty())
        return;

    tools::Rectangle aTmp(rRect);
    rRect.SetTop( aTmp.Left() );
    rRect.SetBottom( aTmp.Right() );
    rRect.SetLeft( aTmp.Top() );
    rRect.SetRight( aTmp.Bottom() );

    if (bRightAligned)
    {
        tools::Long nRef = 2 * nReference;
        rRect.SetLeft( nRef - rRect.Left() );
        rRect.SetRight( nRef - rRect.Right() );
    }
}

static void ImplDrawRulerTab(vcl::RenderContext& rRenderContext, const Point& rPos,
                              sal_uInt16 nStyle, WinBits nWinBits)
{
    if (nStyle & RULER_STYLE_INVISIBLE)
        return;

    sal_uInt16 nTabStyle = nStyle & RULER_TAB_STYLE;
    bool bRTL = 0 != (nStyle & RULER_TAB_RTL);

    // Scale by the screen DPI scaling factor
    // However when doing this some of the rectangles
    // drawn become asymmetric due to the +1 offsets
    sal_uInt16 DPIOffset = rRenderContext.GetDPIScaleFactor() - 1;

    // A tabstop is drawn using three rectangles
    tools::Rectangle aRect1; // A horizontal short line
    tools::Rectangle aRect2; // A vertical short line
    tools::Rectangle aRect3; // A small square

    aRect3.SetEmpty();

    if (nTabStyle == RULER_TAB_DEFAULT)
    {
        aRect1.SetLeft( rPos.X() - ruler_tab.dwidth2 + 1 );
        aRect1.SetTop( rPos.Y() - ruler_tab.dheight2 + 1 );
        aRect1.SetRight( rPos.X() - ruler_tab.dwidth2 + ruler_tab.dwidth + DPIOffset );
        aRect1.SetBottom( rPos.Y() );

        aRect2.SetLeft( rPos.X() - ruler_tab.dwidth2 + ruler_tab.dwidth3 );
        aRect2.SetTop( rPos.Y() - ruler_tab.dheight + 1 );
        aRect2.SetRight( rPos.X() - ruler_tab.dwidth2 + ruler_tab.dwidth3 + ruler_tab.dwidth4 - 1 );
        aRect2.SetBottom( rPos.Y() );

    }
    else if ((!bRTL && nTabStyle == RULER_TAB_LEFT) || (bRTL && nTabStyle == RULER_TAB_RIGHT))
    {
        aRect1.SetLeft( rPos.X() );
        aRect1.SetTop( rPos.Y() - ruler_tab.height2 + 1 );
        aRect1.SetRight( rPos.X() + ruler_tab.width - 1 );
        aRect1.SetBottom( rPos.Y() );

        aRect2.SetLeft( rPos.X() );
        aRect2.SetTop( rPos.Y() - ruler_tab.height + 1 );
        aRect2.SetRight( rPos.X() + ruler_tab.width2 - 1 );
        aRect2.SetBottom( rPos.Y() );
    }
    else if ((!bRTL && nTabStyle == RULER_TAB_RIGHT) || (bRTL && nTabStyle == RULER_TAB_LEFT))
    {
        aRect1.SetLeft( rPos.X() - ruler_tab.width + 1 );
        aRect1.SetTop( rPos.Y() - ruler_tab.height2 + 1 );
        aRect1.SetRight( rPos.X() );
        aRect1.SetBottom( rPos.Y() );

        aRect2.SetLeft( rPos.X() - ruler_tab.width2 + 1 );
        aRect2.SetTop( rPos.Y() - ruler_tab.height + 1 );
        aRect2.SetRight( rPos.X() );
        aRect2.SetBottom( rPos.Y() );
    }
    else
    {
        aRect1.SetLeft( rPos.X() - ruler_tab.cwidth2 + 1 );
        aRect1.SetTop( rPos.Y() - ruler_tab.height2 + 1 );
        aRect1.SetRight( rPos.X() - ruler_tab.cwidth2 + ruler_tab.cwidth + DPIOffset );
        aRect1.SetBottom( rPos.Y() );

        aRect2.SetLeft( rPos.X() - ruler_tab.cwidth2 + ruler_tab.cwidth3 );
        aRect2.SetTop( rPos.Y() - ruler_tab.height + 1 );
        aRect2.SetRight( rPos.X() - ruler_tab.cwidth2 + ruler_tab.cwidth3 + ruler_tab.cwidth4 - 1 );
        aRect2.SetBottom( rPos.Y() );

        if (nTabStyle == RULER_TAB_DECIMAL)
        {
            aRect3.SetLeft( rPos.X() - ruler_tab.cwidth2 + ruler_tab.cwidth - 1 );
            aRect3.SetTop( rPos.Y() - ruler_tab.height + 1 + 1 - DPIOffset );
            aRect3.SetRight( rPos.X() - ruler_tab.cwidth2 + ruler_tab.cwidth + DPIOffset );
            aRect3.SetBottom( rPos.Y() - ruler_tab.height + 1 + 2 );
        }
    }
    if (0 == (nWinBits & WB_HORZ))
    {
        bool bRightAligned = 0 != (nWinBits & WB_RIGHT_ALIGNED);
        lcl_RotateRect_Impl(aRect1, rPos.Y(), bRightAligned);
        lcl_RotateRect_Impl(aRect2, rPos.Y(), bRightAligned);
        lcl_RotateRect_Impl(aRect3, rPos.Y(), bRightAligned);
    }
    rRenderContext.DrawRect(aRect1);
    rRenderContext.DrawRect(aRect2);

    if (!aRect3.IsEmpty())
        rRenderContext.DrawRect(aRect3);
}

void Ruler::ImplDrawTab(vcl::RenderContext& rRenderContext, const Point& rPos, sal_uInt16 nStyle)
{
    if (nStyle & RULER_STYLE_INVISIBLE)
        return;

    rRenderContext.SetLineColor();

    if (nStyle & RULER_STYLE_DONTKNOW)
        rRenderContext.SetFillColor(rRenderContext.GetSettings().GetStyleSettings().GetFaceColor());
    else
        rRenderContext.SetFillColor(rRenderContext.GetSettings().GetStyleSettings().GetDarkShadowColor());

    if (mpData->bTextRTL)
        nStyle |= RULER_TAB_RTL;

    ImplDrawRulerTab(rRenderContext, rPos, nStyle, GetStyle());
}

void Ruler::ImplDrawTabs(vcl::RenderContext& rRenderContext, tools::Long nMin, tools::Long nMax, tools::Long nVirTop, tools::Long nVirBottom)
{
    for (const RulerTab & rTab : mpData->pTabs)
    {
        if (rTab.nStyle & RULER_STYLE_INVISIBLE)
            continue;

        tools::Long aPosition;
        aPosition = rTab.nPos;
        aPosition += +mpData->nNullVirOff;
        tools::Long nTopBottom = (GetStyle() & WB_RIGHT_ALIGNED) ? nVirTop : nVirBottom;
        if (nMin <= aPosition && aPosition <= nMax)
            ImplDrawTab(rRenderContext, Point( aPosition, nTopBottom ), rTab.nStyle);
    }
}

static int adjustSize(int nOrig)
{
    if (nOrig <= 0)
        return 0;

    // make sure we return an odd number, that looks better in the ruler
    return ( (3*nOrig) / 8) * 2 + 1;
}

void Ruler::ApplySettings(vcl::RenderContext& rRenderContext)
{
    const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();

    vcl::Font aFont = rStyleSettings.GetToolFont();
    // make the font a bit smaller than default
    Size aSize(adjustSize(aFont.GetFontSize().Width()), adjustSize(aFont.GetFontSize().Height()));
    aFont.SetFontSize(aSize);

    ApplyControlFont(rRenderContext, aFont);

    ApplyControlForeground(*GetOutDev(), rStyleSettings.GetDarkShadowColor());
    SetTextFillColor();

    Color aColor;
    svtools::ColorConfig aColorConfig;
    aColor = aColorConfig.GetColorValue(svtools::APPBACKGROUND).nColor;
    ApplyControlBackground(rRenderContext, aColor);
    // A hack to get it to change the non-ruler application background to change immediately
    if (aColor != maVirDev->GetBackground().GetColor())
    {
        maVirDev->SetBackground(aColor);
        Resize();
    }
}

void Ruler::ImplInitSettings(bool bFont, bool bForeground, bool bBackground)
{
    const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();

    if (bFont)
    {
        vcl::Font aFont = rStyleSettings.GetToolFont();
        // make the font a bit smaller than default
        Size aSize(adjustSize(aFont.GetFontSize().Width()), adjustSize(aFont.GetFontSize().Height()));
        aFont.SetFontSize(aSize);

        ApplyControlFont(*GetOutDev(), aFont);
    }

    if (bForeground || bFont)
    {
        ApplyControlForeground(*GetOutDev(), rStyleSettings.GetDarkShadowColor());
        SetTextFillColor();
    }

    if (bBackground)
    {
        Color aColor;
        svtools::ColorConfig aColorConfig;
        aColor = aColorConfig.GetColorValue(svtools::APPBACKGROUND).nColor;
        ApplyControlBackground(*GetOutDev(), aColor);
    }

    maVirDev->SetSettings( GetSettings() );
    maVirDev->SetBackground( GetBackground() );
    vcl::Font aFont = GetFont();

    if (mnWinStyle & WB_VERT)
        aFont.SetOrientation(900_deg10);

    maVirDev->SetFont(aFont);
    maVirDev->SetTextColor(GetTextColor());
    maVirDev->SetTextFillColor(GetTextFillColor());
}

void Ruler::ImplCalc()
{
    // calculate offset
    mpData->nRulVirOff = mnWinOff + mpData->nPageOff;
    if ( mpData->nRulVirOff > mnVirOff )
        mpData->nRulVirOff -= mnVirOff;
    else
        mpData->nRulVirOff = 0;
    tools::Long nRulWinOff = mpData->nRulVirOff+mnVirOff;

    // calculate non-visual part of the page
    tools::Long nNotVisPageWidth;
    if ( mpData->nPageOff < 0 )
    {
        nNotVisPageWidth = -(mpData->nPageOff);
        if ( nRulWinOff < mnWinOff )
            nNotVisPageWidth -= mnWinOff-nRulWinOff;
    }
    else
        nNotVisPageWidth = 0;

    // calculate width
    if ( mnWinStyle & WB_HORZ )
    {
        if ( mbAutoWinWidth )
            mnWinWidth = mnWidth - mnVirOff;
        if ( mpData->bAutoPageWidth )
            mpData->nPageWidth = mnWinWidth;
        mpData->nRulWidth = std::min( mnWinWidth, mpData->nPageWidth-nNotVisPageWidth );
        if ( nRulWinOff+mpData->nRulWidth > mnWidth )
            mpData->nRulWidth = mnWidth-nRulWinOff;
    }
    else
    {
        if ( mbAutoWinWidth )
            mnWinWidth = mnHeight - mnVirOff;
        if ( mpData->bAutoPageWidth )
            mpData->nPageWidth = mnWinWidth;
        mpData->nRulWidth = std::min( mnWinWidth, mpData->nPageWidth-nNotVisPageWidth );
        if ( nRulWinOff+mpData->nRulWidth > mnHeight )
            mpData->nRulWidth = mnHeight-nRulWinOff;
    }

    mbCalc = false;
}

void Ruler::ImplFormat(vcl::RenderContext const & rRenderContext)
{
    // if already formatted, don't do it again
    if (!mbFormat)
        return;

    // don't do anything if the window still has no size
    if (!mnVirWidth)
        return;

    const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
    tools::Long    nP1;            // pixel position of Page1
    tools::Long    nP2;            // pixel position of Page2
    tools::Long    nM1;            // pixel position of Margin1
    tools::Long    nM2;            // pixel position of Margin2
    tools::Long    nVirTop;        // top/left corner
    tools::Long    nVirBottom;     // bottom/right corner
    tools::Long    nVirLeft;       // left/top corner
    tools::Long    nVirRight;      // right/bottom corner
    tools::Long    nNullVirOff;    // for faster calculation

    // calculate values
    if (mbCalc)
        ImplCalc();

    mpData->nNullVirOff = mnWinOff + mpData->nPageOff + mpData->nNullOff - mnVirOff;

    nNullVirOff = mpData->nNullVirOff;
    nVirLeft    = mpData->nRulVirOff;
    nVirRight   = nVirLeft + mpData->nRulWidth - 1;
    nVirTop     = 0;
    nVirBottom  = mnVirHeight - 1;

    if (!IsReallyVisible())
        return;

    Size aVirDevSize;

    // initialize VirtualDevice
    if (mnWinStyle & WB_HORZ)
    {
        aVirDevSize.setWidth( mnVirWidth );
        aVirDevSize.setHeight( mnVirHeight );
    }
    else
    {
        aVirDevSize.setHeight( mnVirWidth );
        aVirDevSize.setWidth( mnVirHeight );
    }
    if (aVirDevSize != maVirDev->GetOutputSizePixel())
        maVirDev->SetOutputSizePixel(aVirDevSize);
    else
        maVirDev->Erase();

    // calculate margins
    if (!(mpData->nMargin1Style & RulerMarginStyle::Invisible))
    {
        nM1 = mpData->nMargin1 + nNullVirOff;
        if (mpData->bAutoPageWidth)
        {
            nP1 = nVirLeft;
            if (nM1 < nVirLeft)
                nP1--;
        }
        else
            nP1 = nNullVirOff - mpData->nNullOff;
    }
    else
    {
        nM1 = nVirLeft-1;
        nP1 = nM1;
    }
    if (!(mpData->nMargin2Style & RulerMarginStyle::Invisible))
    {
        nM2 = mpData->nMargin2 + nNullVirOff;
        if (mpData->bAutoPageWidth)
        {
            nP2 = nVirRight;
            if (nM2 > nVirRight)
                nP2++;
        }
        else
            nP2 = nNullVirOff - mpData->nNullOff + mpData->nPageWidth;
        if (nM2 > nP2)
            nM2 = nP2;
    }
    else
    {
        nM2 = nVirRight+1;
        nP2 = nM2;
    }

    // top/bottom border
    maVirDev->SetLineColor(rStyleSettings.GetShadowColor());
    ImplVDrawLine(*maVirDev, nVirLeft, nVirTop + 1, nM1,     nVirTop + 1); //top left line
    ImplVDrawLine(*maVirDev, nM2,      nVirTop + 1, nP2 - 1, nVirTop + 1); //top right line

    nVirTop++;
    nVirBottom--;

    Color aRulerColor;
    Color aMarginRulerColor;
    if (ThemeColors::IsThemeEnabled())
    {
        aRulerColor = ThemeColors::GetThemeColors().GetBaseColor();
        aMarginRulerColor = ThemeColors::GetThemeColors().GetWindowColor();
    }
    else
    {
        aRulerColor = rStyleSettings.GetWindowColor();
        aMarginRulerColor = rStyleSettings.GetDialogColor();
    }

    // draw margin1, margin2 and in-between
    maVirDev->SetLineColor();
    maVirDev->SetFillColor(aMarginRulerColor);
    if (nM1 > nVirLeft)
        ImplVDrawRect(*maVirDev, nP1, nVirTop + 1, nM1, nVirBottom); //left gray rectangle
    if (nM2 < nP2)
        ImplVDrawRect(*maVirDev, nM2, nVirTop + 1, nP2, nVirBottom); //right gray rectangle
    if (nM2 - nM1 > 0)
    {
        maVirDev->SetFillColor(aRulerColor);
        ImplVDrawRect(*maVirDev, nM1 + 1, nVirTop, nM2 - 1, nVirBottom); //center rectangle
    }
    maVirDev->SetLineColor(rStyleSettings.GetShadowColor());
    if (nM1 > nVirLeft)
    {
        ImplVDrawLine(*maVirDev, nM1, nVirTop + 1, nM1, nVirBottom); //right line of the left rectangle
        ImplVDrawLine(*maVirDev, nP1, nVirBottom,  nM1, nVirBottom); //bottom line of the left rectangle
        if (nP1 >= nVirLeft)
        {
            ImplVDrawLine(*maVirDev, nP1, nVirTop + 1, nP1,     nVirBottom); //left line of the left rectangle
            ImplVDrawLine(*maVirDev, nP1, nVirBottom,  nP1 + 1, nVirBottom); //?
        }
    }
    if (nM2 < nP2)
    {
        ImplVDrawLine(*maVirDev, nM2, nVirBottom,  nP2 - 1, nVirBottom); //bottom line of the right rectangle
        ImplVDrawLine(*maVirDev, nM2, nVirTop + 1, nM2,     nVirBottom); //left line of the right rectangle
        if (nP2 <= nVirRight + 1)
            ImplVDrawLine(*maVirDev, nP2 - 1, nVirTop + 1, nP2 - 1, nVirBottom); //right line of the right rectangle
    }

    tools::Long nMin = nVirLeft;
    tools::Long nMax = nP2;
    tools::Long nStart = 0;

    if (mpData->bTextRTL)
        nStart = mpData->nRightFrameMargin + nNullVirOff;
    else
        nStart = mpData->nLeftFrameMargin + nNullVirOff;

    if (nP1 > nVirLeft)
        nMin++;

    if (nP2 < nVirRight)
        nMax--;

    // Draw captions
    ImplDrawTicks(*maVirDev, nMin, nMax, nStart, nVirTop, nVirBottom);

    // Draw borders
    if (!mpData->pBorders.empty())
        ImplDrawBorders(*maVirDev, nVirLeft, nP2, nVirTop, nVirBottom);

    // Draw indents
    if (!mpData->pIndents.empty())
        ImplDrawIndents(*maVirDev, nVirLeft, nP2, nVirTop - 1, nVirBottom + 1);

    // Tabs
    if (!mpData->pTabs.empty())
        ImplDrawTabs(*maVirDev, nVirLeft, nP2, nVirTop-1, nVirBottom + 1);

    mbFormat = false;
}

void Ruler::ImplInitExtraField( bool bUpdate )
{
    Size aWinSize = GetOutputSizePixel();

    // extra field evaluate
    if ( mnWinStyle & WB_EXTRAFIELD )
    {
        maExtraRect.SetLeft( RULER_OFF );
        maExtraRect.SetTop( RULER_OFF );
        maExtraRect.SetRight( RULER_OFF + mnVirHeight - 1 );
        maExtraRect.SetBottom( RULER_OFF + mnVirHeight - 1 );
        if(mpData->bTextRTL)
        {
            if(mnWinStyle & WB_HORZ)
                maExtraRect.Move(aWinSize.Width() - maExtraRect.GetWidth() - maExtraRect.Left(), 0);
            else
                maExtraRect.Move(0, aWinSize.Height() - maExtraRect.GetHeight() - maExtraRect.Top());
            mnVirOff = 0;
        }
        else
            mnVirOff = maExtraRect.Right()+1;

    }
    else
    {
        maExtraRect.SetEmpty();
        mnVirOff = 0;
    }

    // mnVirWidth depends on mnVirOff
    if ( (mnVirWidth > RULER_MIN_SIZE) ||
     ((aWinSize.Width() > RULER_MIN_SIZE) && (aWinSize.Height() > RULER_MIN_SIZE)) )
    {
        if ( mnWinStyle & WB_HORZ )
            mnVirWidth = aWinSize.Width()-mnVirOff;
        else
            mnVirWidth = aWinSize.Height()-mnVirOff;

        if ( mnVirWidth < RULER_MIN_SIZE )
            mnVirWidth = 0;
    }

    if ( bUpdate )
    {
        mbCalc      = true;
        mbFormat    = true;
        Invalidate();
    }
}

void Ruler::ImplDraw(vcl::RenderContext& rRenderContext)
{
    if (mbFormat)
    {
        ImplFormat(rRenderContext);
    }

    if (!IsReallyVisible())
        return;

    // output the ruler to the virtual device
    Point aOffPos;
    Size aVirDevSize = maVirDev->GetOutputSizePixel();

    if (mnWinStyle & WB_HORZ)
    {
        aOffPos.setX( mnVirOff );
        if (mpData->bTextRTL)
            aVirDevSize.AdjustWidth( -(maExtraRect.GetWidth()) );

        aOffPos.setY( RULER_OFF );
    }
    else
    {
        aOffPos.setX( RULER_OFF );
        aOffPos.setY( mnVirOff );
    }
    rRenderContext.DrawOutDev(aOffPos, aVirDevSize, Point(), aVirDevSize, *maVirDev);

    // redraw positionlines
    ImplInvertLines(rRenderContext);
}

void Ruler::ImplDrawExtra(vcl::RenderContext& rRenderContext)
{
    const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
    tools::Rectangle aRect = maExtraRect;
    bool bEraseRect = false;

    aRect.AdjustLeft(2 );
    aRect.AdjustTop(2 );
    aRect.AdjustRight( -2 );
    aRect.AdjustBottom( -2 );

    if (mnExtraStyle & RULER_STYLE_HIGHLIGHT)
    {
        rRenderContext.SetFillColor(rStyleSettings.GetCheckedColor());
        bEraseRect = true;
    }

    if (bEraseRect)
    {
        rRenderContext.SetLineColor();
        rRenderContext.DrawRect(aRect);
    }

    // output content
    if (meExtraType == RulerExtra::NullOffset)
    {
        rRenderContext.SetLineColor(rStyleSettings.GetButtonTextColor());
        rRenderContext.DrawLine(Point(aRect.Left() + 1, aRect.Top() + 4),
                                Point(aRect.Right() - 1, aRect.Top() + 4));
        rRenderContext.DrawLine(Point(aRect.Left() + 4, aRect.Top() + 1),
                                Point(aRect.Left() + 4, aRect.Bottom() - 1));
    }
    else if (meExtraType == RulerExtra::Tab)
    {
        sal_uInt16 nTabStyle = mnExtraStyle & RULER_TAB_STYLE;
        if (mpData->bTextRTL)
            nTabStyle |= RULER_TAB_RTL;
        Point aCenter = aRect.Center();
        Point aDraw(aCenter);
        ImplCenterTabPos(aDraw, nTabStyle);
        WinBits nWinBits = GetStyle();
        if (0 == (nWinBits & WB_HORZ))
        {
            if ((nWinBits & WB_RIGHT_ALIGNED) != 0)
                aDraw.setY( 2 * aCenter.Y() - aDraw.Y() );

            if (mpData->bTextRTL)
            {
                tools::Long nTemp = aDraw.X();
                aDraw.setX( aDraw.Y() );
                aDraw.setY( nTemp );
            }
        }
        ImplDrawTab(rRenderContext, aDraw, nTabStyle);
    }
}

void Ruler::ImplUpdate( bool bMustCalc )
{
    // clear lines in this place so they aren't considered at recalculation
    if (!mbFormat)
        Invalidate(InvalidateFlags::NoErase);

    // set flags
    if (bMustCalc)
        mbCalc = true;
    mbFormat = true;

    // abort if we are dragging as drag-handler will update the ruler after drag is finished
    if (mbDrag)
        return;

    // otherwise trigger update
    if (IsReallyVisible() && IsUpdateMode())
    {
        Invalidate(InvalidateFlags::NoErase);
    }
}

bool Ruler::ImplDoHitTest( const Point& rPos, RulerSelection* pHitTest,
                         bool bRequireStyle, RulerIndentStyle nRequiredStyle,
                         tools::Long nTolerance ) const
{
    sal_Int32   i;
    sal_uInt16  nStyle;
    tools::Long        nHitBottom;
    tools::Long        nX;
    tools::Long        nY;
    tools::Long        n1;

    if ( !mbActive )
        return false;

    // determine positions
    bool bIsHori = 0 != (mnWinStyle & WB_HORZ);
    if ( bIsHori )
    {
        nX = rPos.X();
        nY = rPos.Y();
    }
    else
    {
        nX = rPos.Y();
        nY = rPos.X();
    }
    nHitBottom = mnVirHeight + (RULER_OFF * 2);

    // #i32608#
    pHitTest->nAryPos = 0;
    pHitTest->mnDragSize = RulerDragSize::Move;
    pHitTest->bSize = false;
    pHitTest->bSizeBar = false;

    // so that leftover tabs and indents are taken into account
    tools::Long nXExtraOff;
    if ( !mpData->pTabs.empty() || !mpData->pIndents.empty() )
        nXExtraOff = (mnVirHeight / 2) - 4;
    else
        nXExtraOff = 0;

    // test if outside
    nX -= mnVirOff;
    if ( (nX < mpData->nRulVirOff - nXExtraOff) ||
         (nX > mpData->nRulVirOff + mpData->nRulWidth + nXExtraOff) ||
         (nY < 0) ||
         (nY > nHitBottom) )
    {
        pHitTest->nPos = 0;
        pHitTest->eType = RulerType::Outside;
        return false;
    }

    nX -= mpData->nNullVirOff;
    pHitTest->nPos  = nX;
    pHitTest->eType = RulerType::DontKnow;

    // first test the tabs
    tools::Rectangle aRect;
    if ( !mpData->pTabs.empty() )
    {
        aRect.SetBottom( nHitBottom );
        aRect.SetTop( aRect.Bottom() - ruler_tab.height - RULER_OFF );

        for ( i = mpData->pTabs.size() - 1; i >= 0; i-- )
        {
            nStyle = mpData->pTabs[i].nStyle;
            if ( !(nStyle & RULER_STYLE_INVISIBLE) )
            {
                nStyle &= RULER_TAB_STYLE;

                // default tabs are only shown (no action)
                if ( nStyle != RULER_TAB_DEFAULT )
                {
                    n1 = mpData->pTabs[i].nPos;

                    if ( nStyle == RULER_TAB_LEFT )
                    {
                        aRect.SetLeft( n1 );
                        aRect.SetRight( n1 + ruler_tab.width - 1 );
                    }
                    else if ( nStyle == RULER_TAB_RIGHT )
                    {
                        aRect.SetRight( n1 );
                        aRect.SetLeft( n1 - ruler_tab.width - 1 );
                    }
                    else
                    {
                        aRect.SetLeft( n1 - ruler_tab.cwidth2 + 1 );
                        aRect.SetRight( n1 - ruler_tab.cwidth2 + ruler_tab.cwidth );
                    }

                    if ( aRect.Contains( Point( nX, nY ) ) )
                    {
                        pHitTest->eType   = RulerType::Tab;
                        pHitTest->nAryPos = i;
                        return true;
                    }
                }
            }
        }
    }

    // Indents
    if ( !mpData->pIndents.empty() )
    {
        tools::Long nIndentHeight = (mnVirHeight / 2) - 1;
        tools::Long nIndentWidth2 = nIndentHeight - 3;

        for ( i = mpData->pIndents.size(); i; i-- )
        {
            RulerIndentStyle nIndentStyle = mpData->pIndents[i-1].nStyle;
            if ( (! bRequireStyle || nIndentStyle == nRequiredStyle) &&
                 !mpData->pIndents[i-1].bInvisible )
            {
                n1 = mpData->pIndents[i-1].nPos;

                if ( (nIndentStyle == RulerIndentStyle::Bottom) != !bIsHori )
                {
                    aRect.SetLeft( n1-nIndentWidth2 );
                    aRect.SetRight( n1+nIndentWidth2 );
                    aRect.SetTop( nHitBottom-nIndentHeight-RULER_OFF+1 );
                    aRect.SetBottom( nHitBottom );
                }
                else
                {
                    aRect.SetLeft( n1-nIndentWidth2 );
                    aRect.SetRight( n1+nIndentWidth2 );
                    aRect.SetTop( 0 );
                    aRect.SetBottom( nIndentHeight+RULER_OFF-1 );
                }

                if ( aRect.Contains( Point( nX, nY ) ) )
                {
                    pHitTest->eType     = RulerType::Indent;
                    pHitTest->nAryPos   = i-1;
                    return true;
                }
            }
        }
    }

    // test the borders
    int nBorderTolerance = nTolerance;
    if(pHitTest->bExpandTest)
    {
        nBorderTolerance++;
    }

    for ( i = mpData->pBorders.size(); i; i-- )
    {
        n1 = mpData->pBorders[i-1].nPos;
        tools::Long n2 = n1 + mpData->pBorders[i-1].nWidth;

        // borders have at least 3 pixel padding
        if ( !mpData->pBorders[i-1].nWidth )
        {
             n1 -= nBorderTolerance;
             n2 += nBorderTolerance;
        }

        if ( (nX >= n1) && (nX <= n2) )
        {
            RulerBorderStyle nBorderStyle = mpData->pBorders[i-1].nStyle;
            if ( !(nBorderStyle & RulerBorderStyle::Invisible) )
            {
                pHitTest->eType     = RulerType::Border;
                pHitTest->nAryPos   = i-1;

                if ( !(nBorderStyle & RulerBorderStyle::Sizeable) )
                {
                    if ( nBorderStyle & RulerBorderStyle::Moveable )
                    {
                        pHitTest->bSizeBar = true;
                        pHitTest->mnDragSize = RulerDragSize::Move;
                    }
                }
                else
                {
                    tools::Long nMOff = RULER_MOUSE_BORDERWIDTH;
                    while ( nMOff*2 >= (n2-n1-RULER_MOUSE_BORDERMOVE) )
                    {
                        if ( nMOff < 2 )
                        {
                            nMOff = 0;
                            break;
                        }
                        else
                            nMOff--;
                    }

                    if ( nX <= n1+nMOff )
                    {
                        pHitTest->bSize = true;
                        pHitTest->mnDragSize = RulerDragSize::N1;
                    }
                    else if ( nX >= n2-nMOff )
                    {
                        pHitTest->bSize = true;
                        pHitTest->mnDragSize = RulerDragSize::N2;
                    }
                    else
                    {
                        if ( nBorderStyle & RulerBorderStyle::Moveable )
                        {
                            pHitTest->bSizeBar = true;
                            pHitTest->mnDragSize = RulerDragSize::Move;
                        }
                    }
                }

                return true;
            }
        }
    }

    // Margins
    int nMarginTolerance = pHitTest->bExpandTest ? nBorderTolerance : RULER_MOUSE_MARGINWIDTH;

    if ( (mpData->nMargin1Style & (RulerMarginStyle::Sizeable | RulerMarginStyle::Invisible)) == RulerMarginStyle::Sizeable )
    {
        n1 = mpData->nMargin1;
        if ( (nX >= n1 - nMarginTolerance) && (nX <= n1 + nMarginTolerance) )
        {
            pHitTest->eType = RulerType::Margin1;
            pHitTest->bSize = true;
            return true;
        }
    }
    if ( (mpData->nMargin2Style & (RulerMarginStyle::Sizeable | RulerMarginStyle::Invisible)) == RulerMarginStyle::Sizeable )
    {
        n1 = mpData->nMargin2;
        if ( (nX >= n1 - nMarginTolerance) && (nX <= n1 + nMarginTolerance) )
        {
            pHitTest->eType = RulerType::Margin2;
            pHitTest->bSize = true;
            return true;
        }
    }

    // test tabs again
    if ( !mpData->pTabs.empty() )
    {
        aRect.SetTop( RULER_OFF );
        aRect.SetBottom( nHitBottom );

        for ( i = mpData->pTabs.size() - 1; i >= 0; i-- )
        {
            nStyle = mpData->pTabs[i].nStyle;
            if ( !(nStyle & RULER_STYLE_INVISIBLE) )
            {
                nStyle &= RULER_TAB_STYLE;

                // default tabs are only shown (no action)
                if ( nStyle != RULER_TAB_DEFAULT )
                {
                    n1 = mpData->pTabs[i].nPos;

                    if ( nStyle == RULER_TAB_LEFT )
                    {
                        aRect.SetLeft( n1 );
                        aRect.SetRight( n1 + ruler_tab.width - 1 );
                    }
                    else if ( nStyle == RULER_TAB_RIGHT )
                    {
                        aRect.SetRight( n1 );
                        aRect.SetLeft( n1 - ruler_tab.width - 1 );
                    }
                    else
                    {
                        aRect.SetLeft( n1 - ruler_tab.cwidth2 + 1 );
                        aRect.SetRight( n1 - ruler_tab.cwidth2 + ruler_tab.cwidth );
                    }

                    aRect.AdjustLeft( -1 );
                    aRect.AdjustRight( 1 );

                    if ( aRect.Contains( Point( nX, nY ) ) )
                    {
                        pHitTest->eType   = RulerType::Tab;
                        pHitTest->nAryPos = i;
                        return true;
                    }
                }
            }
        }
    }

    return false;
}

bool Ruler::ImplDocHitTest( const Point& rPos, RulerType eDragType,
                                RulerSelection* pHitTest, tools::Long nTolerance ) const
{
    Point aPos = rPos;
    bool bRequiredStyle = false;
    RulerIndentStyle nRequiredStyle = RulerIndentStyle::Top;

    if (eDragType == RulerType::Indent)
    {
        bRequiredStyle = true;
        nRequiredStyle = RulerIndentStyle::Bottom;
    }

    if ( mnWinStyle & WB_HORZ )
        aPos.AdjustX(mnWinOff );
    else
        aPos.AdjustY(mnWinOff );

    if ( (eDragType == RulerType::Indent) || (eDragType == RulerType::DontKnow) )
    {
        if ( mnWinStyle & WB_HORZ )
            aPos.setY( RULER_OFF + 1 );
        else
            aPos.setX( RULER_OFF + 1 );

        if ( ImplDoHitTest( aPos, pHitTest, bRequiredStyle, nRequiredStyle, nTolerance ) )
        {
            if ( (pHitTest->eType == eDragType) || (eDragType == RulerType::DontKnow) )
                return true;
        }
    }

    if ( (eDragType == RulerType::Indent) ||
         (eDragType == RulerType::Tab) ||
         (eDragType == RulerType::DontKnow) )
    {
        if ( mnWinStyle & WB_HORZ )
            aPos.setY( mnHeight - RULER_OFF - 1 );
        else
            aPos.setX( mnWidth - RULER_OFF - 1 );

        if ( ImplDoHitTest( aPos, pHitTest, bRequiredStyle, nRequiredStyle, nTolerance ) )
        {
            if ( (pHitTest->eType == eDragType) || (eDragType == RulerType::DontKnow) )
                return true;
        }
    }

    if ( (eDragType == RulerType::Margin1) || (eDragType == RulerType::Margin2) ||
         (eDragType == RulerType::Border) || (eDragType == RulerType::DontKnow) )
    {
        if ( mnWinStyle & WB_HORZ )
            aPos.setY( RULER_OFF + (mnVirHeight / 2) );
        else
            aPos.setX( RULER_OFF + (mnVirHeight / 2) );

        if ( ImplDoHitTest( aPos, pHitTest, false, RulerIndentStyle::Top, nTolerance ) )
        {
            if ( (pHitTest->eType == eDragType) || (eDragType == RulerType::DontKnow) )
                return true;
        }
    }

    pHitTest->eType = RulerType::DontKnow;

    return false;
}

bool Ruler::ImplStartDrag( RulerSelection const * pHitTest, sal_uInt16 nModifier )
{
    // don't trigger drag if a border that was clicked can not be changed
    if ( (pHitTest->eType == RulerType::Border) &&
         !pHitTest->bSize && !pHitTest->bSizeBar )
        return false;

    // Set drag data
    meDragType      = pHitTest->eType;
    mnDragPos       = pHitTest->nPos;
    mnDragAryPos    = pHitTest->nAryPos;
    mnDragSize      = pHitTest->mnDragSize;
    mnDragModifier  = nModifier;
    *mpDragData     = *mpSaveData;
    mpData          = mpDragData.get();

    // call handler
    if (StartDrag())
    {
        // if the handler allows dragging, initialize dragging
        mbDrag = true;
        mnStartDragPos = mnDragPos;
        StartTracking();
        Invalidate(InvalidateFlags::NoErase);
        return true;
    }
    else
    {
        // otherwise reset the data
        meDragType      = RulerType::DontKnow;
        mnDragPos       = 0;
        mnDragAryPos    = 0;
        mnDragSize      = RulerDragSize::Move;
        mnDragModifier  = 0;
        mpData          = mpSaveData.get();
    }

    return false;
}

void Ruler::ImplDrag( const Point& rPos )
{
    tools::Long  nX;
    tools::Long  nY;
    tools::Long  nOutHeight;

    if ( mnWinStyle & WB_HORZ )
    {
        nX          = rPos.X();
        nY          = rPos.Y();
        nOutHeight  = mnHeight;
    }
    else
    {
        nX          = rPos.Y();
        nY          = rPos.X();
        nOutHeight  = mnWidth;
    }

    // calculate and fit X
    nX -= mnVirOff;
    if ( nX < mpData->nRulVirOff )
    {
        nX = mpData->nRulVirOff;
    }
    else if ( nX > mpData->nRulVirOff+mpData->nRulWidth )
    {
        nX = mpData->nRulVirOff+mpData->nRulWidth;
    }
    nX -= mpData->nNullVirOff;

    // if upper or left from ruler, then consider old values
    mbDragDelete = false;
    if ( nY < 0 )
    {
        if ( !mbDragCanceled )
        {
            // reset the data
            mbDragCanceled = true;
            ImplRulerData aTempData = *mpDragData;
            *mpDragData = *mpSaveData;
            mbCalc = true;
            mbFormat = true;

            // call handler
            mnDragPos = mnStartDragPos;
            Drag();

            // and redraw
            Invalidate(InvalidateFlags::NoErase);

            // reset the data as before cancel
            *mpDragData = std::move(aTempData);
        }
    }
    else
    {
        mbDragCanceled = false;

        // +2, so the tabs are not cleared too quickly
        if ( nY > nOutHeight + 2 )
            mbDragDelete = true;

        mnDragPos = nX;

        // call handler
        Drag();

        // redraw
        if (mbFormat)
            Invalidate(InvalidateFlags::NoErase);
    }
}

void Ruler::ImplEndDrag()
{
    // get values
    if ( mbDragCanceled )
        *mpDragData = *mpSaveData;
    else
        *mpSaveData = *mpDragData;

    mpData = mpSaveData.get();
    mbDrag = false;

    // call handler
    EndDrag();

    // reset drag values
    meDragType      = RulerType::DontKnow;
    mnDragPos       = 0;
    mnDragAryPos    = 0;
    mnDragSize      = RulerDragSize::Move;
    mbDragCanceled  = false;
    mbDragDelete    = false;
    mnDragModifier  = 0;
    mnStartDragPos  = 0;

    // redraw
    Invalidate(InvalidateFlags::NoErase);
}

void Ruler::MouseButtonDown( const MouseEvent& rMEvt )
{
    if ( !rMEvt.IsLeft() || IsTracking() )
        return;

    Point   aMousePos = rMEvt.GetPosPixel();
    sal_uInt16  nMouseClicks = rMEvt.GetClicks();
    sal_uInt16  nMouseModifier = rMEvt.GetModifier();

    // update ruler
    if ( mbFormat )
    {
        Invalidate(InvalidateFlags::NoErase);
    }

    if ( maExtraRect.Contains( aMousePos ) )
    {
        ExtraDown();
    }
    else
    {
        RulerSelection aHitTest;
        bool bHitTestResult = ImplDoHitTest(aMousePos, &aHitTest);

        if ( nMouseClicks == 1 )
        {
            if ( bHitTestResult )
            {
                ImplStartDrag( &aHitTest, nMouseModifier );
            }
            else
            {
                // calculate position inside of ruler area
                if ( aHitTest.eType == RulerType::DontKnow )
                {
                    mnDragPos = aHitTest.nPos;
                    Click();
                    mnDragPos = 0;

                    // call HitTest again as a click, for example, could set a new tab
                    if ( ImplDoHitTest(aMousePos, &aHitTest) )
                        ImplStartDrag(&aHitTest, nMouseModifier);
                }
            }
        }
        else
        {
            if (bHitTestResult)
            {
                mnDragPos    = aHitTest.nPos;
                mnDragAryPos = aHitTest.nAryPos;
            }
            meDragType = aHitTest.eType;

            DoubleClick();

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

--> maximum size reached

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

Messung V0.5
C=92 H=96 G=93

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