/* -*- 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/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( true, true, true );
 
    // 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();
    mxAccContext.clear();
    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--;
 
    // draw margin1, margin2 and in-between
    maVirDev->SetLineColor();
    maVirDev->SetFillColor(rStyleSettings.GetDialogColor());
    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(rStyleSettings.GetWindowColor());
        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();
 
            meDragType      = RulerType::DontKnow;
            mnDragPos       = 0;
            mnDragAryPos    = 0;
        }
    }
}
 
void Ruler::MouseMove( const MouseEvent& rMEvt )
{
    PointerStyle ePtrStyle = PointerStyle::Arrow;
 
    mxPreviousHitTest.swap(mxCurrentHitTest);
 
    mxCurrentHitTest.reset(new RulerSelection);
 
    maHoverSelection.eType = RulerType::DontKnow;
 
    if (ImplDoHitTest( rMEvt.GetPosPixel(), mxCurrentHitTest.get() ))
    {
        maHoverSelection = *mxCurrentHitTest;
 
        if (mxCurrentHitTest->bSize)
        {
            if (mnWinStyle & WB_HORZ)
            {
                if (mxCurrentHitTest->mnDragSize == RulerDragSize::N1)
                    ePtrStyle = PointerStyle::TabSelectW;
                else if (mxCurrentHitTest->mnDragSize == RulerDragSize::N2)
                    ePtrStyle = PointerStyle::TabSelectE;
                else
                    ePtrStyle = PointerStyle::ESize;
            }
            else
            {
                if (mxCurrentHitTest->mnDragSize == RulerDragSize::N1)
                    ePtrStyle = PointerStyle::WindowNSize;
                else if (mxCurrentHitTest->mnDragSize == RulerDragSize::N2)
                    ePtrStyle = PointerStyle::WindowSSize;
                else
                    ePtrStyle = PointerStyle::SSize;
            }
        }
        else if (mxCurrentHitTest->bSizeBar)
        {
            if (mnWinStyle & WB_HORZ)
                ePtrStyle = PointerStyle::HSizeBar;
            else
                ePtrStyle = PointerStyle::VSizeBar;
        }
    }
 
    if (mxPreviousHitTest != nullptr && mxPreviousHitTest->eType != mxCurrentHitTest->eType)
    {
        mbFormat = true;
    }
 
    SetPointer( ePtrStyle );
 
    if (mbFormat)
    {
        Invalidate(InvalidateFlags::NoErase);
    }
}
 
void Ruler::Tracking( const TrackingEvent& rTEvt )
{
    if ( rTEvt.IsTrackingEnded() )
    {
        // reset the old state at cancel
        if ( rTEvt.IsTrackingCanceled() )
        {
            mbDragCanceled = true;
            mbFormat       = true;
        }
 
        ImplEndDrag();
    }
    else
        ImplDrag( rTEvt.GetMouseEvent().GetPosPixel() );
}
 
void Ruler::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
{
    ImplDraw(rRenderContext);
 
    // consider extra field
    if (mnWinStyle & WB_EXTRAFIELD)
        ImplDrawExtra(rRenderContext);
}
 
void Ruler::Resize()
{
    Size aWinSize = GetOutputSizePixel();
 
    tools::Long nNewHeight;
    if ( mnWinStyle & WB_HORZ )
    {
        if ( aWinSize.Height() != mnHeight )
            nNewHeight = aWinSize.Height();
        else
            nNewHeight = 0;
    }
    else
    {
        if ( aWinSize.Width() != mnWidth )
            nNewHeight = aWinSize.Width();
        else
            nNewHeight = 0;
    }
 
    mbFormat = true;
 
    // clear lines
    bool bVisible = IsReallyVisible();
    if ( bVisible && !mpData->pLines.empty() )
    {
        mnUpdateFlags |= RULER_UPDATE_LINES;
        Invalidate(InvalidateFlags::NoErase);
    }
 
    // recalculate some values if the height/width changes
    // extra field should always be updated
    ImplInitExtraField( mpData->bTextRTL );
    if ( nNewHeight )
    {
        mbCalc = true;
        mnVirHeight = nNewHeight - mnBorderWidth - ( RULER_OFF * 2 );
    }
    else
    {
        if ( mpData->bAutoPageWidth )
            ImplUpdate( true );
        else if ( mbAutoWinWidth )
            mbCalc = true;
    }
 
    // clear part of the border
    if ( bVisible )
    {
        if ( nNewHeight )
            Invalidate(InvalidateFlags::NoErase);
        else if ( mpData->bAutoPageWidth )
        {
            // only at AutoPageWidth do we need to redraw
            tools::Rectangle aRect;
 
            if ( mnWinStyle & WB_HORZ )
            {
                if ( mnWidth < aWinSize.Width() )
                    aRect.SetLeft( mnWidth - RULER_RESIZE_OFF );
                else
                    aRect.SetLeft( aWinSize.Width() - RULER_RESIZE_OFF );
                aRect.SetRight( aRect.Left() + RULER_RESIZE_OFF );
                aRect.SetTop( RULER_OFF );
                aRect.SetBottom( RULER_OFF + mnVirHeight );
            }
            else
            {
                if ( mnHeight < aWinSize.Height() )
                    aRect.SetTop( mnHeight-RULER_RESIZE_OFF );
                else
                    aRect.SetTop( aWinSize.Height()-RULER_RESIZE_OFF );
                aRect.SetBottom( aRect.Top() + RULER_RESIZE_OFF );
                aRect.SetLeft( RULER_OFF );
                aRect.SetRight( RULER_OFF + mnVirHeight );
            }
 
            Invalidate(aRect, InvalidateFlags::NoErase);
        }
    }
 
    mnWidth  = aWinSize.Width();
    mnHeight = aWinSize.Height();
}
 
void Ruler::StateChanged( StateChangedType nType )
{
    Window::StateChanged( nType );
 
    if ( nType == StateChangedType::InitShow )
        Invalidate();
    else if ( nType == StateChangedType::UpdateMode )
    {
        if ( IsReallyVisible() && IsUpdateMode() )
            Invalidate();
    }
    else if ( (nType == StateChangedType::Zoom) ||
              (nType == StateChangedType::ControlFont) )
    {
        ImplInitSettings( true, false, false );
        Invalidate();
    }
    else if ( nType == StateChangedType::ControlForeground )
    {
        ImplInitSettings( false, true, false );
        Invalidate();
    }
    else if ( nType == StateChangedType::ControlBackground )
    {
        ImplInitSettings( false, false, true );
        Invalidate();
    }
}
 
void Ruler::DataChanged( const DataChangedEvent& rDCEvt )
{
    Window::DataChanged( rDCEvt );
 
    if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
         (rDCEvt.GetType() == DataChangedEventType::DISPLAY) ||
         (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
         ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
          (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
    {
        mbFormat = true;
        ImplInitSettings( true, true, true );
        Invalidate();
    }
}
 
bool Ruler::StartDrag()
{
    return false;
}
 
void Ruler::Drag()
{
}
 
void Ruler::EndDrag()
{
}
 
void Ruler::Click()
{
}
 
void Ruler::DoubleClick()
{
    maDoubleClickHdl.Call( this );
}
 
void Ruler::ExtraDown()
{
}
 
void Ruler::Activate()
{
    mbActive = true;
 
    // update positionlines - draw is delayed
    mnUpdateFlags |= RULER_UPDATE_LINES;
    Invalidate(InvalidateFlags::NoErase);
}
 
void Ruler::Deactivate()
{
    // clear positionlines
    Invalidate(InvalidateFlags::NoErase);
 
    mbActive = false;
}
 
bool Ruler::StartDocDrag( const MouseEvent& rMEvt, RulerType eDragType, tools::Long nTolerance )
{
    if ( !mbDrag )
    {
        Point          aMousePos = rMEvt.GetPosPixel();
        sal_uInt16     nMouseClicks = rMEvt.GetClicks();
        sal_uInt16     nMouseModifier = rMEvt.GetModifier();
        RulerSelection aHitTest;
 
        if(eDragType != RulerType::DontKnow)
            aHitTest.bExpandTest = true;
 
        // update ruler
        if ( mbFormat )
        {
            if (!IsReallyVisible())
            {
                // set mpData for ImplDocHitTest()
                ImplFormat(*GetOutDev());
            }
 
            Invalidate(InvalidateFlags::NoErase);
        }
 
        if ( nMouseClicks == 1 )
        {
            if ( ImplDocHitTest( aMousePos, eDragType, &aHitTest, nTolerance ) )
            {
                PointerStyle aPtr = PointerStyle::Arrow;
 
                if ( aHitTest.bSize )
                {
                    if ( mnWinStyle & WB_HORZ )
                        aPtr = PointerStyle::ESize;
                    else
                        aPtr = PointerStyle::SSize;
                }
                else if ( aHitTest.bSizeBar )
                {
                    if ( mnWinStyle & WB_HORZ )
                        aPtr = PointerStyle::HSizeBar;
                    else
                        aPtr = PointerStyle::VSizeBar;
                }
                SetPointer( aPtr );
                return ImplStartDrag( &aHitTest, nMouseModifier );
            }
        }
        else if ( nMouseClicks == 2 )
        {
            if ( ImplDocHitTest( aMousePos, eDragType, &aHitTest, nTolerance ) )
            {
                mnDragPos    = aHitTest.nPos;
                mnDragAryPos = aHitTest.nAryPos;
            }
 
            DoubleClick();
 
            mnDragPos       = 0;
            mnDragAryPos    = 0;
 
            return true;
        }
    }
 
    return false;
}
 
void Ruler::CancelDrag()
{
    if ( mbDrag )
    {
        ImplDrag( Point( -1, -1 ) );
        ImplEndDrag();
    }
}
 
RulerType Ruler::GetRulerType( const Point& rPos, sal_uInt16* pAryPos )
{
    RulerSelection aHitTest;
 
    // update ruler
    if ( IsReallyVisible() && mbFormat )
    {
        Invalidate(InvalidateFlags::NoErase);
    }
 
    (void)ImplDoHitTest(rPos, &aHitTest);
 
    // return values
    if ( pAryPos )
        *pAryPos = aHitTest.nAryPos;
    return aHitTest.eType;
}
 
void Ruler::SetWinPos( tools::Long nNewOff, tools::Long nNewWidth )
{
    // should widths be automatically calculated
    if ( !nNewWidth )
        mbAutoWinWidth = true;
    else
        mbAutoWinWidth = false;
 
    mnWinOff = nNewOff;
    mnWinWidth = nNewWidth;
    ImplUpdate( true );
}
 
void Ruler::SetPagePos( tools::Long nNewOff, tools::Long nNewWidth )
{
    // should we do anything?
    if ( (mpData->nPageOff == nNewOff) && (mpData->nPageWidth == nNewWidth) )
        return;
 
    // should widths be automatically calculated
    if ( !nNewWidth )
        mpData->bAutoPageWidth = true;
    else
        mpData->bAutoPageWidth = false;
 
    mpData->nPageOff     = nNewOff;
    mpData->nPageWidth   = nNewWidth;
    ImplUpdate( true );
}
 
void Ruler::SetBorderPos( tools::Long nOff )
{
    if ( mnWinStyle & WB_BORDER )
    {
        if ( mnBorderOff != nOff )
        {
            mnBorderOff = nOff;
 
            if ( IsReallyVisible() && IsUpdateMode() )
                Invalidate(InvalidateFlags::NoErase);
        }
    }
}
 
void Ruler::SetUnit( FieldUnit eNewUnit )
{
    if ( meUnit == eNewUnit )
        return;
 
    meUnit = eNewUnit;
    switch ( meUnit )
    {
        case FieldUnit::MM:
            mnUnitIndex = RULER_UNIT_MM;
            break;
        case FieldUnit::CM:
            mnUnitIndex = RULER_UNIT_CM;
            break;
        case FieldUnit::M:
            mnUnitIndex = RULER_UNIT_M;
            break;
        case FieldUnit::KM:
            mnUnitIndex = RULER_UNIT_KM;
            break;
        case FieldUnit::INCH:
            mnUnitIndex = RULER_UNIT_INCH;
            break;
        case FieldUnit::FOOT:
            mnUnitIndex = RULER_UNIT_FOOT;
            break;
        case FieldUnit::MILE:
            mnUnitIndex = RULER_UNIT_MILE;
            break;
        case FieldUnit::POINT:
            mnUnitIndex = RULER_UNIT_POINT;
            break;
        case FieldUnit::PICA:
            mnUnitIndex = RULER_UNIT_PICA;
            break;
        case FieldUnit::CHAR:
            mnUnitIndex = RULER_UNIT_CHAR;
            break;
        case FieldUnit::LINE:
            mnUnitIndex = RULER_UNIT_LINE;
            break;
        default:
            SAL_WARN( "svtools.control", "Ruler::SetUnit() - Wrong Unit" );
            break;
    }
 
    maMapMode.SetMapUnit( aImplRulerUnitTab[mnUnitIndex].eMapUnit );
    ImplUpdate();
}
 
void Ruler::SetZoom( const Fraction& rNewZoom )
{
    DBG_ASSERT( rNewZoom.GetNumerator(), "Ruler::SetZoom() with scale 0 is not allowed" );
 
    if ( maZoom != rNewZoom )
    {
        maZoom = rNewZoom;
        maMapMode.SetScaleX( maZoom );
        maMapMode.SetScaleY( maZoom );
        ImplUpdate();
    }
}
 
void Ruler::SetExtraType( RulerExtra eNewExtraType, sal_uInt16 nStyle )
{
    if ( mnWinStyle & WB_EXTRAFIELD )
    {
        meExtraType  = eNewExtraType;
        mnExtraStyle = nStyle;
        if (IsReallyVisible() && IsUpdateMode())
            Invalidate();
    }
}
 
void Ruler::SetNullOffset( tools::Long nPos )
{
    if ( mpData->nNullOff != nPos )
    {
        mpData->nNullVirOff += nPos - mpData->nNullOff;
        mpData->nNullOff = nPos;
        ImplUpdate();
    }
}
 
void Ruler::SetLeftFrameMargin( tools::Long nPos )
{
    if ( mpData->nLeftFrameMargin != nPos )
    {
        mpData->nLeftFrameMargin  = nPos;
        ImplUpdate();
    }
}
 
void Ruler::SetRightFrameMargin( tools::Long nPos )
{
    if ( mpData->nRightFrameMargin != nPos )
    {
        mpData->nRightFrameMargin  = nPos;
        ImplUpdate();
    }
}
 
void Ruler::SetMargin1( tools::Long nPos, RulerMarginStyle nMarginStyle )
{
    if ( (mpData->nMargin1 != nPos) || (mpData->nMargin1Style != nMarginStyle) )
    {
        mpData->nMargin1      = nPos;
        mpData->nMargin1Style = nMarginStyle;
        ImplUpdate();
    }
}
 
void Ruler::SetMargin2( tools::Long nPos, RulerMarginStyle nMarginStyle )
{
    DBG_ASSERT( (nPos >= mpData->nMargin1) ||
                (mpData->nMargin1Style & RulerMarginStyle::Invisible) ||
                (mpData->nMargin2Style & RulerMarginStyle::Invisible),
                "Ruler::SetMargin2() - Margin2 < Margin1" );
 
    if ( (mpData->nMargin2 != nPos) || (mpData->nMargin2Style != nMarginStyle) )
    {
        mpData->nMargin2      = nPos;
        mpData->nMargin2Style = nMarginStyle;
        ImplUpdate();
    }
}
 
void Ruler::SetLines( sal_uInt32 aLineArraySize, const RulerLine* pLineArray )
{
    // To determine if what has changed
    if ( mpData->pLines.size() == aLineArraySize )
    {
        sal_uInt32           i = aLineArraySize;
        std::vector<RulerLine>::const_iterator aItr1 = mpData->pLines.begin();
        const RulerLine* pAry2 = pLineArray;
        while ( i )
        {
            if ( aItr1->nPos   != pAry2->nPos )
                break;
            ++aItr1;
            ++pAry2;
            i--;
        }
        if ( !i )
            return;
    }
 
    // New values and new share issue
    bool bMustUpdate;
    bMustUpdate = IsReallyVisible() && IsUpdateMode();
 
    // Delete old lines
    if ( bMustUpdate )
        Invalidate(InvalidateFlags::NoErase);
 
    // New data set
    if ( !aLineArraySize || !pLineArray )
    {
        if ( mpData->pLines.empty() )
            return;
        mpData->pLines.clear();
    }
    else
    {
        if ( mpData->pLines.size() != aLineArraySize )
        {
            mpData->pLines.resize(aLineArraySize);
        }
 
        std::copy( pLineArray,
                   pLineArray + aLineArraySize,
                   mpData->pLines.begin() );
 
        if ( bMustUpdate )
            Invalidate(InvalidateFlags::NoErase);
    }
}
 
void Ruler::SetBorders( sal_uInt32 aBorderArraySize, const RulerBorder* pBorderArray )
{
    if ( !aBorderArraySize || !pBorderArray )
    {
        if ( mpData->pBorders.empty() )
            return;
        mpData->pBorders.clear();
    }
    else
    {
        if ( mpData->pBorders.size() != aBorderArraySize )
        {
            mpData->pBorders.resize(aBorderArraySize);
        }
        else
        {
            sal_uInt32             i = aBorderArraySize;
            const RulerBorder* pAry1 = mpData->pBorders.data();
            const RulerBorder* pAry2 = pBorderArray;
            while ( i )
            {
                if ( (pAry1->nPos   != pAry2->nPos)   ||
                     (pAry1->nWidth != pAry2->nWidth) ||
                     (pAry1->nStyle != pAry2->nStyle) )
                    break;
                pAry1++;
                pAry2++;
                i--;
            }
            if ( !i )
                return;
        }
        std::copy( pBorderArray,
                   pBorderArray + aBorderArraySize,
                   mpData->pBorders.begin() );
    }
 
    ImplUpdate();
}
 
void Ruler::SetIndents( sal_uInt32 aIndentArraySize, const RulerIndent* pIndentArray )
{
 
    if ( !aIndentArraySize || !pIndentArray )
    {
        if ( mpData->pIndents.empty() )
            return;
        mpData->pIndents.clear();
    }
    else
    {
        if ( mpData->pIndents.size() != aIndentArraySize )
        {
            mpData->pIndents.resize(aIndentArraySize);
        }
        else
        {
            sal_uInt32             i = aIndentArraySize;
            const RulerIndent* pAry1 = mpData->pIndents.data();
            const RulerIndent* pAry2 = pIndentArray;
            while ( i )
            {
                if ( (pAry1->nPos   != pAry2->nPos) ||
                     (pAry1->nStyle != pAry2->nStyle) )
                    break;
                pAry1++;
                pAry2++;
                i--;
            }
            if ( !i )
                return;
        }
 
        std::copy( pIndentArray,
                   pIndentArray + aIndentArraySize,
                   mpData->pIndents.begin() );
    }
 
    ImplUpdate();
}
 
void Ruler::SetTabs( sal_uInt32 aTabArraySize, const RulerTab* pTabArray )
{
    if ( aTabArraySize == 0 || pTabArray == nullptr )
    {
        if ( mpData->pTabs.empty() )
            return;
        mpData->pTabs.clear();
    }
    else
    {
        if ( mpData->pTabs.size() != aTabArraySize )
        {
            mpData->pTabs.resize(aTabArraySize);
        }
        else
        {
            sal_uInt32 i = aTabArraySize;
            std::vector<RulerTab>::iterator aTabIterator = mpData->pTabs.begin();
            const RulerTab* pInputArray = pTabArray;
            while ( i )
            {
                RulerTab& aCurrent = *aTabIterator;
                if ( aCurrent.nPos   != pInputArray->nPos ||
                     aCurrent.nStyle != pInputArray->nStyle )
                {
                    break;
                }
                ++aTabIterator;
                pInputArray++;
                i--;
            }
            if ( !i )
                return;
        }
        std::copy(pTabArray, pTabArray + aTabArraySize, mpData->pTabs.begin());
    }
 
    ImplUpdate();
}
 
const std::vector<RulerTab>& Ruler::GetTabs() const
{
    return mpData->pTabs;
}
 
void Ruler::SetStyle( WinBits nStyle )
{
    if ( mnWinStyle != nStyle )
    {
        mnWinStyle = nStyle;
        ImplInitExtraField( true );
    }
}
 
void Ruler::DrawTab(vcl::RenderContext& rRenderContext, const Color &rFillColor, const Point& rPos, sal_uInt16 nStyle)
{
    Point aPos(rPos);
    sal_uInt16 nTabStyle = nStyle & (RULER_TAB_STYLE | RULER_TAB_RTL);
 
    rRenderContext.Push(vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR);
    rRenderContext.SetLineColor();
    rRenderContext.SetFillColor(rFillColor);
    ImplCenterTabPos(aPos, nTabStyle);
    ImplDrawRulerTab(rRenderContext, aPos, nTabStyle, nStyle);
    rRenderContext.Pop();
}
 
void Ruler::SetTextRTL(bool bRTL)
{
    if(mpData->bTextRTL != bRTL)
    {
        mpData->bTextRTL = bRTL;
        if ( IsReallyVisible() && IsUpdateMode() )
            ImplInitExtraField( true );
    }
 
}
 
tools::Long Ruler::GetPageOffset() const
{
    return mpData->nPageOff;
}
 
tools::Long Ruler::GetNullOffset() const
{
    return mpData->nNullOff;
}
 
tools::Long Ruler::GetMargin1() const
{
    return mpData->nMargin1;
}
 
tools::Long Ruler::GetMargin2() const
{
    return mpData->nMargin2;
}
 
const RulerUnitData& Ruler::GetCurrentRulerUnit() const
{
    return aImplRulerUnitTab[mnUnitIndex];
}
 
void Ruler::DrawTicks()
{
    mbFormat = true;
    Invalidate(InvalidateFlags::NoErase);
}
 
uno::Reference< XAccessible > Ruler::CreateAccessible()
{
    vcl::Window* pParent = GetAccessibleParentWindow();
    OSL_ENSURE( pParent, "-SvxRuler::CreateAccessible(): No Parent!" );
    uno::Reference< XAccessible >   xAccParent  = pParent->GetAccessible();
    if( xAccParent.is() )
    {
        // MT: Fixed compiler issue because the address from a temporary object was used.
        // BUT: Should it really be a Pointer, instead of const&???
        OUString aStr;
        if ( mnWinStyle & WB_HORZ )
        {
            aStr = SvtResId(STR_SVT_ACC_RULER_HORZ_NAME);
        }
        else
        {
            aStr = SvtResId(STR_SVT_ACC_RULER_VERT_NAME);
        }
        mxAccContext = new SvtRulerAccessible( xAccParent, *this, aStr );
        SetAccessible(mxAccContext);
        return mxAccContext;
    }
    else
        return uno::Reference< XAccessible >();
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V1053 Calling the 'EnableRTL' virtual function indirectly in the constructor may lead to unexpected result at runtime. Check lines: 'ruler.cxx:276', 'ruler.cxx:184', 'window.hxx:1516'.

V636 The '(nBottom - nTop) / 2' expression was implicitly cast from 'long' type to 'double' type. Consider utilizing an explicit type cast to avoid the loss of a fractional part. An example: double A = (double)(X) / Y;.

V730 It is possible that not all members of a class are initialized inside the constructor. Consider inspecting: mnStartDragPos.