/* -*- 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 <sal/log.hxx>
#include <comphelper/string.hxx>
#include <vcl/event.hxx>
#include <vcl/decoview.hxx>
#include <vcl/glyphitemcache.hxx>
#include <vcl/svapp.hxx>
#include <vcl/help.hxx>
#include <vcl/vcllayout.hxx>
#include <vcl/status.hxx>
#include <vcl/virdev.hxx>
#include <vcl/settings.hxx>
#include <config_features.h>
#include <svdata.hxx>
#include <window.h>
 
#define STATUSBAR_OFFSET_X      STATUSBAR_OFFSET
#define STATUSBAR_OFFSET_Y      2
#define STATUSBAR_OFFSET_TEXTY  3
 
#define STATUSBAR_PRGS_OFFSET   3
#define STATUSBAR_PRGS_COUNT    100
#define STATUSBAR_PRGS_MIN      5
 
#define STATUSBAR_MIN_HEIGHT    16 // icons height, tdf#153344
 
class StatusBar::ImplData
{
public:
    ImplData();
 
    VclPtr<VirtualDevice> mpVirDev;
};
 
StatusBar::ImplData::ImplData()
{
    mpVirDev = nullptr;
}
 
struct ImplStatusItem
{
    sal_uInt16                          mnId;
    StatusBarItemBits                   mnBits;
    tools::Long                                mnWidth;
    tools::Long                                mnOffset;
    tools::Long                                mnExtraWidth;
    tools::Long                                mnX;
    OUString                            maText;
    OUString                            maHelpText;
    OUString                            maQuickHelpText;
    OUString                            maHelpId;
    void*                               mpUserData;
    bool                                mbVisible;
    OUString                            maAccessibleName;
    OUString                            maCommand;
    std::optional<SalLayoutGlyphs>      mLayoutGlyphsCache;
    SalLayoutGlyphs*                    GetTextGlyphs(const OutputDevice* pOutputDevice);
};
 
SalLayoutGlyphs* ImplStatusItem::GetTextGlyphs(const OutputDevice* outputDevice)
{
    if(!mLayoutGlyphsCache.has_value())
    {
        std::unique_ptr<SalLayout> pSalLayout = outputDevice->ImplLayout(
            maText, 0, -1, Point(0, 0), 0, {}, {}, SalLayoutFlags::GlyphItemsOnly);
        mLayoutGlyphsCache = pSalLayout ? pSalLayout->GetGlyphs() : SalLayoutGlyphs();
    }
    return mLayoutGlyphsCache->IsValid() ? &mLayoutGlyphsCache.value() : nullptr;
}
 
static tools::Long ImplCalcProgressWidth( sal_uInt16 nMax, tools::Long nSize )
{
    return ((nMax*(nSize+(nSize/2)))-(nSize/2)+(STATUSBAR_PRGS_OFFSET*2));
}
 
static Point ImplGetItemTextPos( const Size& rRectSize, const Size& rTextSize,
                                 StatusBarItemBits nStyle )
{
    tools::Long nX;
    tools::Long nY;
    tools::Long delta = (rTextSize.Height()/4) + 1;
    if( delta + rTextSize.Width() > rRectSize.Width() )
        delta = 0;
 
    if ( nStyle & StatusBarItemBits::Left )
        nX = delta;
    else if ( nStyle & StatusBarItemBits::Right )
        nX = rRectSize.Width()-rTextSize.Width()-delta;
    else // StatusBarItemBits::Center
        nX = (rRectSize.Width()-rTextSize.Width())/2;
    nY = (rRectSize.Height()-rTextSize.Height())/2 + 1;
    return Point( nX, nY );
}
 
bool StatusBar::ImplIsItemUpdate() const
{
    return !mbProgressMode && IsReallyVisible() && IsUpdateMode();
}
 
void StatusBar::ImplInit( vcl::Window* pParent, WinBits nStyle )
{
    mpImplData.reset(new ImplData);
 
    // default: RightAlign
    if ( !(nStyle & (WB_LEFT | WB_RIGHT)) )
        nStyle |= WB_RIGHT;
 
    Window::ImplInit( pParent, nStyle & ~WB_BORDER, nullptr );
 
    // remember WinBits
    mpImplData->mpVirDev = VclPtr<VirtualDevice>::Create( *GetOutDev() );
    mnCurItemId     = 0;
    mbFormat        = true;
    mbProgressMode  = false;
    mbInUserDraw    = false;
    mbAdjustHiDPI   = false;
    mnItemsWidth    = STATUSBAR_OFFSET_X;
    mnDX            = 0;
    mnDY            = 0;
    mnCalcHeight    = 0;
    mnTextY         = STATUSBAR_OFFSET_TEXTY;
 
    ImplInitSettings();
 
    SetOutputSizePixel( CalcWindowSizePixel() );
}
 
StatusBar::StatusBar( vcl::Window* pParent, WinBits nStyle ) :
    Window( WindowType::STATUSBAR ),
    mnLastProgressPaint_ms(osl_getGlobalTimer())
{
    ImplInit( pParent, nStyle );
}
 
StatusBar::~StatusBar()
{
    disposeOnce();
}
 
void StatusBar::dispose()
{
    // delete all items
    mvItemList.clear();
 
    // delete VirtualDevice
    mpImplData->mpVirDev.disposeAndClear();
    mpImplData.reset();
    Window::dispose();
}
 
void StatusBar::AdjustItemWidthsForHiDPI()
{
    mbAdjustHiDPI = true;
}
 
void StatusBar::ApplySettings(vcl::RenderContext& rRenderContext)
{
    rRenderContext.SetLineColor();
 
    const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
    ApplyControlFont(rRenderContext, rStyleSettings.GetToolFont());
 
    Color aColor;
    if (IsControlForeground())
        aColor = GetControlForeground();
    else if (GetStyle() & WB_3DLOOK)
        aColor = rStyleSettings.GetButtonTextColor();
    else
        aColor = rStyleSettings.GetWindowTextColor();
    rRenderContext.SetTextColor(aColor);
 
    rRenderContext.SetTextFillColor();
 
    if (IsControlBackground())
        aColor = GetControlBackground();
    else if (GetStyle() & WB_3DLOOK)
        aColor = rStyleSettings.GetFaceColor();
    else
        aColor = rStyleSettings.GetWindowColor();
    rRenderContext.SetBackground(aColor);
 
    // NWF background
    if (!IsControlBackground() &&
          rRenderContext.IsNativeControlSupported(ControlType::WindowBackground, ControlPart::BackgroundWindow))
    {
        ImplGetWindowImpl()->mnNativeBackground = ControlPart::BackgroundWindow;
        EnableChildTransparentMode();
    }
}
 
void StatusBar::ImplInitSettings()
{
    ApplySettings(*GetOutDev());
 
    mpImplData->mpVirDev->SetFont(GetFont());
    mpImplData->mpVirDev->SetTextColor(GetTextColor());
    mpImplData->mpVirDev->SetTextAlign(GetTextAlign());
    mpImplData->mpVirDev->SetTextFillColor();
    mpImplData->mpVirDev->SetBackground(GetBackground());
}
 
void StatusBar::ImplFormat()
{
    tools::Long            nExtraWidth;
    tools::Long            nExtraWidth2;
    tools::Long            nX;
    sal_uInt16      nAutoSizeItems;
    bool            bChanged;
 
    do {
        // sum up widths
        nAutoSizeItems = 0;
        mnItemsWidth = STATUSBAR_OFFSET_X;
        bChanged = false;
        tools::Long nOffset = 0;
        for ( const auto & pItem : mvItemList ) {
            if ( pItem->mbVisible )
            {
                if ( pItem->mnBits & StatusBarItemBits::AutoSize ) {
                    nAutoSizeItems++;
                }
 
                mnItemsWidth += pItem->mnWidth + nOffset;
                nOffset = pItem->mnOffset;
            }
        }
 
        if ( mnDX > 0 && mnDX < mnItemsWidth )
        {
            // Total width of items is more than available width
            // Try to hide secondary elements, if any
            for ( auto & pItem : mvItemList )
            {
                if ( pItem->mbVisible && !(pItem->mnBits & StatusBarItemBits::Mandatory) )
                {
                    pItem->mbVisible = false;
                    bChanged = true;
                    break;
                }
            }
        }
        else if ( mnDX > mnItemsWidth )
        {
            // Width of statusbar is sufficient.
            // Try to restore hidden items, if any
            for ( auto & pItem : mvItemList )
            {
                if ( !pItem->mbVisible &&
                    !(pItem->mnBits & StatusBarItemBits::Mandatory) &&
                    pItem->mnWidth + nOffset + mnItemsWidth < mnDX )
                {
                    pItem->mbVisible = true;
                    bChanged = true;
                    break;
                }
            }
        }
    } while ( bChanged );
 
    if ( GetStyle() & WB_RIGHT )
    {
        // AutoSize isn't computed for right-alignment,
        // because we show the text that is declared by SetText on the left side
        nX              = mnDX - mnItemsWidth;
        nExtraWidth     = 0;
        nExtraWidth2    = 0;
    }
    else
    {
        mnItemsWidth += STATUSBAR_OFFSET_X;
 
        // calling AutoSize is potentially necessary for left-aligned text,
        if ( nAutoSizeItems && (mnDX > (mnItemsWidth - STATUSBAR_OFFSET)) )
        {
            nExtraWidth  = (mnDX - mnItemsWidth - 1) / nAutoSizeItems;
            nExtraWidth2 = (mnDX - mnItemsWidth - 1) % nAutoSizeItems;
        }
        else
        {
            nExtraWidth  = 0;
            nExtraWidth2 = 0;
        }
        nX = STATUSBAR_OFFSET_X;
 
        if( GetOutDev()->HasMirroredGraphics() && IsRTLEnabled() )
            nX += ImplGetSVData()->maNWFData.mnStatusBarLowerRightOffset;
    }
 
    for (auto & pItem : mvItemList) {
        if ( pItem->mbVisible ) {
            if ( pItem->mnBits & StatusBarItemBits::AutoSize ) {
                pItem->mnExtraWidth = nExtraWidth;
                if ( nExtraWidth2 ) {
                    pItem->mnExtraWidth++;
                    nExtraWidth2--;
                }
            } else {
                pItem->mnExtraWidth = 0;
            }
 
            pItem->mnX = nX;
            nX += pItem->mnWidth + pItem->mnExtraWidth + pItem->mnOffset;
        }
    }
 
    mbFormat = false;
}
 
tools::Rectangle StatusBar::ImplGetItemRectPos( sal_uInt16 nPos ) const
{
    tools::Rectangle       aRect;
    ImplStatusItem* pItem = ( nPos < mvItemList.size() ) ? mvItemList[ nPos ].get() : nullptr;
    if ( pItem && pItem->mbVisible )
    {
        aRect.SetLeft( pItem->mnX );
        aRect.SetRight( aRect.Left() + pItem->mnWidth + pItem->mnExtraWidth );
        aRect.SetTop( STATUSBAR_OFFSET_Y );
        aRect.SetBottom( mnCalcHeight - STATUSBAR_OFFSET_Y );
    }
 
    return aRect;
}
 
sal_uInt16 StatusBar::ImplGetFirstVisiblePos() const
{
    for( size_t nPos = 0; nPos < mvItemList.size(); nPos++ )
    {
        ImplStatusItem* pItem = mvItemList[ nPos ].get();
        if ( pItem->mbVisible )
            return sal_uInt16(nPos);
    }
 
    return SAL_MAX_UINT16;
}
 
void StatusBar::ImplDrawText(vcl::RenderContext& rRenderContext)
{
    // prevent item box from being overwritten
    tools::Rectangle aTextRect;
    aTextRect.SetLeft( STATUSBAR_OFFSET_X + 1 );
    aTextRect.SetTop( mnTextY );
    if (GetStyle() & WB_RIGHT)
        aTextRect.SetRight( mnDX - mnItemsWidth - 1 );
    else
        aTextRect.SetRight( mnDX - 1 );
    if (aTextRect.Right() > aTextRect.Left())
    {
        // compute position
        OUString aStr = GetText();
        sal_Int32 nPos = aStr.indexOf('\n');
        if (nPos != -1)
            aStr = aStr.copy(0, nPos);
 
        aTextRect.SetBottom( aTextRect.Top()+GetTextHeight()+1 );
 
        rRenderContext.DrawText(aTextRect, aStr, DrawTextFlags::Left | DrawTextFlags::Top | DrawTextFlags::Clip | DrawTextFlags::EndEllipsis);
    }
}
 
void StatusBar::ImplDrawItem(vcl::RenderContext& rRenderContext, bool bOffScreen, sal_uInt16 nPos)
{
    tools::Rectangle aRect = ImplGetItemRectPos(nPos);
 
    if (aRect.IsEmpty())
        return;
 
    // compute output region
    ImplStatusItem* pItem = mvItemList[nPos].get();
    tools::Long nW = 1;
    tools::Rectangle aTextRect(aRect.Left() + nW, aRect.Top() + nW,
                        aRect.Right() - nW, aRect.Bottom() - nW);
 
    Size aTextRectSize(aTextRect.GetSize());
 
    if (bOffScreen)
    {
        mpImplData->mpVirDev->SetOutputSizePixel(aTextRectSize);
    }
    else
    {
        vcl::Region aRegion(aTextRect);
        rRenderContext.SetClipRegion(aRegion);
    }
 
    // if the framework code is drawing status, let it do all the work
    if (!(pItem->mnBits & StatusBarItemBits::UserDraw))
    {
        SalLayoutGlyphs* pGlyphs = pItem->GetTextGlyphs(&rRenderContext);
        Size aTextSize(rRenderContext.GetTextWidth(pItem->maText,0,-1,nullptr,pGlyphs),
                       rRenderContext.GetTextHeight());
        Point aTextPos = ImplGetItemTextPos(aTextRectSize, aTextSize, pItem->mnBits);
 
        if (bOffScreen)
        {
            mpImplData->mpVirDev->DrawText(
                        aTextPos,
                        pItem->maText,
                        0, -1, nullptr, nullptr,
                        pGlyphs );
        }
        else
        {
            aTextPos.AdjustX(aTextRect.Left() );
            aTextPos.AdjustY(aTextRect.Top() );
            rRenderContext.DrawText(
                        aTextPos,
                        pItem->maText,
                        0, -1, nullptr, nullptr,
                        pGlyphs );
        }
    }
 
    // call DrawItem if necessary
    if (pItem->mnBits & StatusBarItemBits::UserDraw)
    {
        if (bOffScreen)
        {
            mbInUserDraw = true;
            mpImplData->mpVirDev->EnableRTL( IsRTLEnabled() );
            UserDrawEvent aODEvt(mpImplData->mpVirDev, tools::Rectangle(Point(), aTextRectSize), pItem->mnId);
            UserDraw(aODEvt);
            mpImplData->mpVirDev->EnableRTL(false);
            mbInUserDraw = false;
        }
        else
        {
            UserDrawEvent aODEvt(&rRenderContext, aTextRect, pItem->mnId);
            UserDraw(aODEvt);
        }
    }
 
    if (bOffScreen)
        rRenderContext.DrawOutDev(aTextRect.TopLeft(), aTextRectSize, Point(), aTextRectSize, *mpImplData->mpVirDev);
    else
        rRenderContext.SetClipRegion();
 
    if (nPos != ImplGetFirstVisiblePos())
    {
        // draw separator
        Point aFrom(aRect.TopLeft());
        aFrom.AdjustX( -4 );
        aFrom.AdjustY( 1 );
        Point aTo(aRect.BottomLeft());
        aTo.AdjustX( -4 );
        aTo.AdjustY( -1 );
 
        DecorationView aDecoView(&rRenderContext);
        aDecoView.DrawSeparator(aFrom, aTo);
    }
 
    if (!rRenderContext.ImplIsRecordLayout())
        CallEventListeners(VclEventId::StatusbarDrawItem, reinterpret_cast<void*>(pItem->mnId));
}
 
void DrawProgress(vcl::Window* pWindow, vcl::RenderContext& rRenderContext, const Point& rPos,
                  tools::Long nOffset, tools::Long nPrgsWidth, tools::Long nPrgsHeight,
                  sal_uInt16 nPercent1, sal_uInt16 nPercent2, sal_uInt16 nPercentCount,
                  const tools::Rectangle& rFramePosSize, ControlType eControlType)
{
    if (rRenderContext.IsNativeControlSupported(eControlType, ControlPart::Entire))
    {
        bool bNeedErase = ImplGetSVData()->maNWFData.mbProgressNeedsErase;
 
        tools::Long nFullWidth = (nPrgsWidth + nOffset) * (10000 / nPercentCount);
        tools::Long nPerc = std::min<sal_uInt16>(nPercent2, 10000);
        ImplControlValue aValue(nFullWidth * nPerc / 10000);
        tools::Rectangle aDrawRect(rPos, Size(nFullWidth, nPrgsHeight));
        tools::Rectangle aControlRegion(aDrawRect);
 
        if(bNeedErase)
        {
            vcl::Window* pEraseWindow = pWindow;
            while (pEraseWindow->IsPaintTransparent() && !pEraseWindow->ImplGetWindowImpl()->mbFrame)
            {
                pEraseWindow = pEraseWindow->ImplGetWindowImpl()->mpParent;
            }
 
            if (pEraseWindow == pWindow)
            {
                // restore background of pWindow
                rRenderContext.Erase(rFramePosSize);
            }
            else
            {
                // restore transparent background
                AbsoluteScreenPixelPoint aTL1(pWindow->OutputToAbsoluteScreenPixel(rFramePosSize.TopLeft()));
                Point aTL = pEraseWindow->AbsoluteScreenToOutputPixel(aTL1);
                tools::Rectangle aRect(aTL, rFramePosSize.GetSize());
                pEraseWindow->Invalidate(aRect, InvalidateFlags::NoChildren     |
                                                InvalidateFlags::NoClipChildren |
                                                InvalidateFlags::Transparent);
                pEraseWindow->PaintImmediately();
            }
            rRenderContext.Push(vcl::PushFlags::CLIPREGION);
            rRenderContext.IntersectClipRegion(rFramePosSize);
        }
 
        bool bNativeOK = rRenderContext.DrawNativeControl(eControlType, ControlPart::Entire, aControlRegion,
                                                          ControlState::ENABLED, aValue, OUString());
        if (bNeedErase)
            rRenderContext.Pop();
        if (bNativeOK)
            return;
    }
 
    if (eControlType == ControlType::LevelBar)
    {
        if (nPercent2 < 2500)
            rRenderContext.SetFillColor({ 0xFF0000 });
        else if (nPercent2 < 5000)
            rRenderContext.SetFillColor({ 0xFFFF00 });
        else if (nPercent2 < 7500)
            rRenderContext.SetFillColor({ 0x0000FF });
        else
            rRenderContext.SetFillColor({ 0x00FF00 });
    }
 
    // precompute values
    sal_uInt16 nPerc1 = nPercent1 / nPercentCount;
    sal_uInt16 nPerc2 = nPercent2 / nPercentCount;
 
    if (nPerc1 > nPerc2)
    {
        // support progress that can also decrease
 
        // compute rectangle
        tools::Long nDX = nPrgsWidth + nOffset;
        tools::Long nLeft = rPos.X() + ((nPerc1 - 1) * nDX);
        tools::Rectangle aRect(nLeft, rPos.Y(), nLeft + nPrgsWidth, rPos.Y() + nPrgsHeight);
 
        do
        {
            rRenderContext.Erase(aRect);
            aRect.AdjustLeft( -nDX );
            aRect.AdjustRight( -nDX );
            nPerc1--;
        }
        while (nPerc1 > nPerc2);
    }
    else if (nPerc1 < nPerc2)
    {
        // draw Percent rectangle
        // if Percent2 greater than 100%, adapt values
        if (nPercent2 > 10000)
        {
            nPerc2 = 10000 / nPercentCount;
            if (nPerc1 >= nPerc2)
                nPerc1 = nPerc2 - 1;
        }
 
        // compute rectangle
        tools::Long nDX = nPrgsWidth + nOffset;
        tools::Long nLeft = rPos.X() + (nPerc1 * nDX);
        tools::Rectangle aRect(nLeft, rPos.Y(), nLeft + nPrgsWidth, rPos.Y() + nPrgsHeight);
 
        do
        {
            rRenderContext.DrawRect(aRect);
            aRect.AdjustLeft(nDX );
            aRect.AdjustRight(nDX );
            nPerc1++;
        }
        while (nPerc1 < nPerc2);
 
        // if greater than 100%, set rectangle to blink
        if (nPercent2 > 10000 && eControlType == ControlType::Progress)
        {
            // define on/off status
            if (((nPercent2 / nPercentCount) & 0x01) == (nPercentCount & 0x01))
            {
                aRect.AdjustLeft( -nDX );
                aRect.AdjustRight( -nDX );
                rRenderContext.Erase(aRect);
            }
        }
    }
}
 
void StatusBar::ImplDrawProgress(vcl::RenderContext& rRenderContext, sal_uInt16 nPercent2)
{
    bool bNative = rRenderContext.IsNativeControlSupported(ControlType::Progress, ControlPart::Entire);
    // bPaint: draw text also, else only update progress
    rRenderContext.DrawText(maPrgsTxtPos, maPrgsTxt);
    if (!bNative)
    {
        DecorationView aDecoView(&rRenderContext);
        aDecoView.DrawFrame(maPrgsFrameRect, DrawFrameStyle::In);
    }
 
    Point aPos(maPrgsFrameRect.Left() + STATUSBAR_PRGS_OFFSET,
               maPrgsFrameRect.Top()  + STATUSBAR_PRGS_OFFSET);
    tools::Long nPrgsHeight = mnPrgsSize;
    if (bNative)
    {
        aPos = maPrgsFrameRect.TopLeft();
        nPrgsHeight = maPrgsFrameRect.GetHeight();
    }
    DrawProgress(this, rRenderContext, aPos, mnPrgsSize / 2, mnPrgsSize, nPrgsHeight,
                 0, nPercent2 * 100, mnPercentCount, maPrgsFrameRect, ControlType::Progress);
}
 
void StatusBar::ImplCalcProgressRect()
{
    // calculate text size
    Size aPrgsTxtSize( GetTextWidth( maPrgsTxt ), GetTextHeight() );
    maPrgsTxtPos.setX( STATUSBAR_OFFSET_X+1 );
 
    // calculate progress frame
    maPrgsFrameRect.SetLeft( maPrgsTxtPos.X()+aPrgsTxtSize.Width()+STATUSBAR_OFFSET );
    maPrgsFrameRect.SetTop( STATUSBAR_OFFSET_Y );
    maPrgsFrameRect.SetBottom( mnCalcHeight - STATUSBAR_OFFSET_Y );
 
    // calculate size of progress rects
    mnPrgsSize = maPrgsFrameRect.Bottom()-maPrgsFrameRect.Top()-(STATUSBAR_PRGS_OFFSET*2);
    sal_uInt16 nMaxPercent = STATUSBAR_PRGS_COUNT;
 
    tools::Long nMaxWidth = mnDX-STATUSBAR_OFFSET-1;
 
    // make smaller if there are too many rects
    while ( maPrgsFrameRect.Left()+ImplCalcProgressWidth( nMaxPercent, mnPrgsSize ) > nMaxWidth )
    {
        nMaxPercent--;
        if ( nMaxPercent <= STATUSBAR_PRGS_MIN )
            break;
    }
    maPrgsFrameRect.SetRight( maPrgsFrameRect.Left() + ImplCalcProgressWidth( nMaxPercent, mnPrgsSize ) );
 
    // save the divisor for later
    mnPercentCount = 10000 / nMaxPercent;
    bool bNativeOK = false;
    if( IsNativeControlSupported( ControlType::Progress, ControlPart::Entire ) )
    {
        ImplControlValue aValue;
        tools::Rectangle aControlRegion( tools::Rectangle( Point(), maPrgsFrameRect.GetSize() ) );
        tools::Rectangle aNativeControlRegion, aNativeContentRegion;
        if( (bNativeOK = GetNativeControlRegion( ControlType::Progress, ControlPart::Entire, aControlRegion,
                                                 ControlState::ENABLED, aValue,
                                                 aNativeControlRegion, aNativeContentRegion ) ) )
        {
            tools::Long nProgressHeight = aNativeControlRegion.GetHeight();
            if( nProgressHeight > maPrgsFrameRect.GetHeight() )
            {
                tools::Long nDelta = nProgressHeight - maPrgsFrameRect.GetHeight();
                maPrgsFrameRect.AdjustTop( -(nDelta - nDelta/2) );
                maPrgsFrameRect.AdjustBottom(nDelta/2 );
            }
            maPrgsTxtPos.setY( maPrgsFrameRect.Top() + (nProgressHeight - GetTextHeight())/2 );
        }
    }
    if( ! bNativeOK )
        maPrgsTxtPos.setY( mnTextY );
}
 
void StatusBar::MouseButtonDown( const MouseEvent& rMEvt )
{
    // trigger toolbox only for left mouse button
    if ( !rMEvt.IsLeft() )
        return;
 
    Point  aMousePos = rMEvt.GetPosPixel();
 
    // search for clicked item
    for ( size_t i = 0; i < mvItemList.size(); ++i )
    {
        ImplStatusItem* pItem = mvItemList[ i ].get();
        // check item for being clicked
        if ( ImplGetItemRectPos( sal_uInt16(i) ).Contains( aMousePos ) )
        {
            mnCurItemId = pItem->mnId;
            if ( rMEvt.GetClicks() == 2 )
                DoubleClick();
            else
                Click();
            mnCurItemId = 0;
 
            // Item found
            return;
        }
    }
 
    // if there's no item, trigger Click or DoubleClick
    if ( rMEvt.GetClicks() == 2 )
        DoubleClick();
    else
        Click();
}
 
void StatusBar::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
{
    if (mbFormat)
        ImplFormat();
 
    sal_uInt16 nItemCount = sal_uInt16( mvItemList.size() );
 
    if (mbProgressMode)
    {
        rRenderContext.Push(vcl::PushFlags::FILLCOLOR | vcl::PushFlags::LINECOLOR);
 
        const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
        Color aProgressColor = rStyleSettings.GetHighlightColor();
        if (aProgressColor == rStyleSettings.GetFaceColor())
            aProgressColor = rStyleSettings.GetDarkShadowColor();
        rRenderContext.SetLineColor();
        rRenderContext.SetFillColor(aProgressColor);
 
        ImplDrawProgress(rRenderContext, mnPercent);
 
        rRenderContext.Pop();
    }
    else
    {
        // draw text
        if (GetStyle() & WB_RIGHT)
            ImplDrawText(rRenderContext);
 
        // draw items
 
        // Do offscreen only when we are not recording layout...
        bool bOffscreen = !rRenderContext.ImplIsRecordLayout();
 
        if (!bOffscreen)
            rRenderContext.Erase(rRect);
 
        for (sal_uInt16 i = 0; i < nItemCount; i++)
            ImplDrawItem(rRenderContext, bOffscreen, i);
    }
 
    // draw line at the top of the status bar (to visually distinguish it from
    // shell / docking area)
    const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
    rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
    rRenderContext.DrawLine(Point(0, 0), Point(mnDX-1, 0));
}
 
void StatusBar::Resize()
{
    // save width and height
    Size aSize = GetOutputSizePixel();
    mnDX = aSize.Width() - ImplGetSVData()->maNWFData.mnStatusBarLowerRightOffset;
    mnDY = aSize.Height();
    mnCalcHeight = mnDY;
 
    mnTextY = (mnCalcHeight-GetTextHeight())/2;
 
    // provoke re-formatting
    mbFormat = true;
 
    if ( mbProgressMode )
        ImplCalcProgressRect();
 
    Invalidate();
}
 
void StatusBar::RequestHelp( const HelpEvent& rHEvt )
{
    // no keyboard help in status bar
    if( rHEvt.KeyboardActivated() )
        return;
 
    sal_uInt16 nItemId = GetItemId( ScreenToOutputPixel( rHEvt.GetMousePosPixel() ) );
 
    if ( nItemId )
    {
        tools::Rectangle aItemRect = GetItemRect( nItemId );
        Point aPt = OutputToScreenPixel( aItemRect.TopLeft() );
        aItemRect.SetLeft( aPt.X() );
        aItemRect.SetTop( aPt.Y() );
        aPt = OutputToScreenPixel( aItemRect.BottomRight() );
        aItemRect.SetRight( aPt.X() );
        aItemRect.SetBottom( aPt.Y() );
 
        if ( rHEvt.GetMode() & HelpEventMode::BALLOON )
        {
            OUString aStr = GetHelpText( nItemId );
            Help::ShowBalloon( this, aItemRect.Center(), aItemRect, aStr );
            return;
        }
        else if ( rHEvt.GetMode() & HelpEventMode::QUICK )
        {
            OUString aStr(GetQuickHelpText(nItemId));
            // show quickhelp if available
            if (!aStr.isEmpty())
            {
                Help::ShowQuickHelp( this, aItemRect, aStr );
                return;
            }
            aStr = GetItemText( nItemId );
            // show a quick help if item text doesn't fit
            if ( GetTextWidth( aStr ) > aItemRect.GetWidth() )
            {
                Help::ShowQuickHelp( this, aItemRect, aStr );
                return;
            }
        }
    }
 
    Window::RequestHelp( rHEvt );
}
 
void StatusBar::StateChanged( StateChangedType nType )
{
    Window::StateChanged( nType );
 
    if ( nType == StateChangedType::InitShow )
        ImplFormat();
    else if ( nType == StateChangedType::UpdateMode )
        Invalidate();
    else if ( (nType == StateChangedType::Zoom) ||
              (nType == StateChangedType::ControlFont) )
    {
        mbFormat = true;
        ImplInitSettings();
        Invalidate();
    }
    else if ( nType == StateChangedType::ControlForeground )
    {
        ImplInitSettings();
        Invalidate();
    }
    else if ( nType == StateChangedType::ControlBackground )
    {
        ImplInitSettings();
        Invalidate();
    }
 
    //invalidate layout cache
    for (auto & pItem : mvItemList)
    {
        pItem->mLayoutGlyphsCache.reset();
    }
 
}
 
void StatusBar::DataChanged( const DataChangedEvent& rDCEvt )
{
    Window::DataChanged( rDCEvt );
 
    if (  !((rDCEvt.GetType() == DataChangedEventType::DISPLAY         )
       || (rDCEvt.GetType() == DataChangedEventType::FONTS           )
       || (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION)
       || (  (rDCEvt.GetType() == DataChangedEventType::SETTINGS)
          && (rDCEvt.GetFlags() & AllSettingsFlags::STYLE )
          ))
       )
        return;
 
    mbFormat = true;
    ImplInitSettings();
    tools::Long nFudge = GetTextHeight() / 4;
    for (auto & pItem : mvItemList)
    {
        tools::Long nWidth = GetTextWidth( pItem->maText ) + nFudge;
        if( nWidth > pItem->mnWidth + STATUSBAR_OFFSET )
            pItem->mnWidth = nWidth + STATUSBAR_OFFSET;
 
        pItem->mLayoutGlyphsCache.reset();
    }
    Size aSize = GetSizePixel();
    // do not disturb current width, since
    // CalcWindowSizePixel calculates a minimum width
    aSize.setHeight( CalcWindowSizePixel().Height() );
    SetSizePixel( aSize );
    Invalidate();
}
 
void StatusBar::Click()
{
    maClickHdl.Call( this );
}
 
void StatusBar::DoubleClick()
{
    maDoubleClickHdl.Call( this );
}
 
void StatusBar::UserDraw( const UserDrawEvent& )
{
}
 
void StatusBar::InsertItem( sal_uInt16 nItemId, sal_uLong nWidth,
                            StatusBarItemBits nBits,
                            tools::Long nOffset, sal_uInt16 nPos )
{
    SAL_WARN_IF( !nItemId, "vcl", "StatusBar::InsertItem(): ItemId == 0" );
    SAL_WARN_IF( GetItemPos( nItemId ) != STATUSBAR_ITEM_NOTFOUND, "vcl",
                "StatusBar::InsertItem(): ItemId already exists" );
 
    // default: IN and CENTER
    if ( !(nBits & (StatusBarItemBits::In | StatusBarItemBits::Out | StatusBarItemBits::Flat)) )
        nBits |= StatusBarItemBits::In;
    if ( !(nBits & (StatusBarItemBits::Left | StatusBarItemBits::Right | StatusBarItemBits::Center)) )
        nBits |= StatusBarItemBits::Center;
 
    // create item
    if (mbAdjustHiDPI)
    {
        nWidth *= GetDPIScaleFactor();
    }
    tools::Long nFudge = GetTextHeight()/4;
    std::unique_ptr<ImplStatusItem> pItem(new ImplStatusItem);
    pItem->mnId             = nItemId;
    pItem->mnBits           = nBits;
    pItem->mnWidth          = static_cast<tools::Long>(nWidth)+nFudge+STATUSBAR_OFFSET;
    pItem->mnOffset         = nOffset;
    pItem->mpUserData       = nullptr;
    pItem->mbVisible        = true;
 
    // add item to list
    if ( nPos < mvItemList.size() ) {
        mvItemList.insert( mvItemList.begin() + nPos, std::move(pItem) );
    } else {
        mvItemList.push_back( std::move(pItem) );
    }
 
    mbFormat = true;
    if ( ImplIsItemUpdate() )
        Invalidate();
 
    CallEventListeners( VclEventId::StatusbarItemAdded, reinterpret_cast<void*>(nItemId) );
}
 
void StatusBar::RemoveItem( sal_uInt16 nItemId )
{
    sal_uInt16 nPos = GetItemPos( nItemId );
    if ( nPos != STATUSBAR_ITEM_NOTFOUND )
    {
        mvItemList.erase( mvItemList.begin() + nPos );
 
        mbFormat = true;
        if ( ImplIsItemUpdate() )
            Invalidate();
 
        CallEventListeners( VclEventId::StatusbarItemRemoved, reinterpret_cast<void*>(nItemId) );
    }
}
 
void StatusBar::ShowItem( sal_uInt16 nItemId )
{
    sal_uInt16 nPos = GetItemPos( nItemId );
 
    if ( nPos == STATUSBAR_ITEM_NOTFOUND )
        return;
 
    ImplStatusItem* pItem = mvItemList[ nPos ].get();
    if ( !pItem->mbVisible )
    {
        pItem->mbVisible = true;
 
        mbFormat = true;
        if ( ImplIsItemUpdate() )
            Invalidate();
 
        CallEventListeners( VclEventId::StatusbarShowItem, reinterpret_cast<void*>(nItemId) );
    }
}
 
void StatusBar::HideItem( sal_uInt16 nItemId )
{
    sal_uInt16 nPos = GetItemPos( nItemId );
 
    if ( nPos == STATUSBAR_ITEM_NOTFOUND )
        return;
 
    ImplStatusItem* pItem = mvItemList[ nPos ].get();
    if ( pItem->mbVisible )
    {
        pItem->mbVisible = false;
 
        mbFormat = true;
        if ( ImplIsItemUpdate() )
            Invalidate();
 
        CallEventListeners( VclEventId::StatusbarHideItem, reinterpret_cast<void*>(nItemId) );
    }
}
 
bool StatusBar::IsItemVisible( sal_uInt16 nItemId ) const
{
    sal_uInt16 nPos = GetItemPos( nItemId );
 
    if ( nPos != STATUSBAR_ITEM_NOTFOUND )
        return mvItemList[ nPos ]->mbVisible;
    else
        return false;
}
 
void StatusBar::Clear()
{
    // delete all items
    mvItemList.clear();
 
    mbFormat = true;
    if ( ImplIsItemUpdate() )
        Invalidate();
 
    CallEventListeners( VclEventId::StatusbarAllItemsRemoved );
}
 
sal_uInt16 StatusBar::GetItemCount() const
{
    return static_cast<sal_uInt16>(mvItemList.size());
}
 
sal_uInt16 StatusBar::GetItemId( sal_uInt16 nPos ) const
{
    if ( nPos < mvItemList.size() )
        return mvItemList[ nPos ]->mnId;
    return 0;
}
 
sal_uInt16 StatusBar::GetItemPos( sal_uInt16 nItemId ) const
{
    for ( size_t i = 0, n = mvItemList.size(); i < n; ++i ) {
        if ( mvItemList[ i ]->mnId == nItemId ) {
            return sal_uInt16( i );
        }
    }
 
    return STATUSBAR_ITEM_NOTFOUND;
}
 
sal_uInt16 StatusBar::GetItemId( const Point& rPos ) const
{
    if ( !mbFormat )
    {
        sal_uInt16 nItemCount = GetItemCount();
        sal_uInt16 nPos;
        for ( nPos = 0; nPos < nItemCount; nPos++ )
        {
            // get rectangle
            tools::Rectangle aRect = ImplGetItemRectPos( nPos );
            if ( aRect.Contains( rPos ) )
                return mvItemList[ nPos ]->mnId;
        }
    }
 
    return 0;
}
 
tools::Rectangle StatusBar::GetItemRect( sal_uInt16 nItemId ) const
{
    tools::Rectangle aRect;
 
    if ( !mbFormat )
    {
        sal_uInt16 nPos = GetItemPos( nItemId );
        if ( nPos != STATUSBAR_ITEM_NOTFOUND )
        {
            // get rectangle and subtract frame
            aRect = ImplGetItemRectPos( nPos );
            tools::Long nW = 1;
            aRect.AdjustTop(nW-1 );
            aRect.AdjustBottom( -(nW-1) );
            aRect.AdjustLeft(nW );
            aRect.AdjustRight( -nW );
            return aRect;
        }
    }
 
    return aRect;
}
 
Point StatusBar::GetItemTextPos( sal_uInt16 nItemId ) const
{
    if ( !mbFormat )
    {
        sal_uInt16 nPos = GetItemPos( nItemId );
        if ( nPos != STATUSBAR_ITEM_NOTFOUND )
        {
            // get rectangle
            ImplStatusItem* pItem = mvItemList[ nPos ].get();
            tools::Rectangle aRect = ImplGetItemRectPos( nPos );
            tools::Long nW = 1;
            tools::Rectangle           aTextRect( aRect.Left()+nW, aRect.Top()+nW,
                                           aRect.Right()-nW, aRect.Bottom()-nW );
            Point aPos = ImplGetItemTextPos( aTextRect.GetSize(),
                                             Size( GetTextWidth( pItem->maText ), GetTextHeight() ),
                                             pItem->mnBits );
            if ( !mbInUserDraw )
            {
                aPos.AdjustX(aTextRect.Left() );
                aPos.AdjustY(aTextRect.Top() );
            }
            return aPos;
        }
    }
 
    return Point();
}
 
sal_uLong StatusBar::GetItemWidth( sal_uInt16 nItemId ) const
{
    sal_uInt16 nPos = GetItemPos( nItemId );
 
    if ( nPos != STATUSBAR_ITEM_NOTFOUND )
        return mvItemList[ nPos ]->mnWidth;
 
    return 0;
}
 
StatusBarItemBits StatusBar::GetItemBits( sal_uInt16 nItemId ) const
{
    sal_uInt16 nPos = GetItemPos( nItemId );
 
    if ( nPos != STATUSBAR_ITEM_NOTFOUND )
        return mvItemList[ nPos ]->mnBits;
 
    return StatusBarItemBits::NONE;
}
 
tools::Long StatusBar::GetItemOffset( sal_uInt16 nItemId ) const
{
    sal_uInt16 nPos = GetItemPos( nItemId );
 
    if ( nPos != STATUSBAR_ITEM_NOTFOUND )
        return mvItemList[ nPos ]->mnOffset;
 
    return 0;
}
 
void StatusBar::PaintSelfAndChildrenImmediately()
{
    WindowImpl* pWindowImpl = ImplGetWindowImpl();
    const bool bOrigOverlapWin = pWindowImpl->mbOverlapWin;
    // Temporarily set mbOverlapWin so that any parent windows of StatusBar
    // that happen to have accumulated Invalidates are not taken as the root
    // paint candidate from which to paint the paint hierarchy. So we limit the
    // paint here to this statusbar and its children, disabling the
    // optimization to bundle pending paints together and suppressing any
    // unexpected side effects of entering parent window paint handlers if this
    // call is not from the primordial thread.
    pWindowImpl->mbOverlapWin = true;
    PaintImmediately();
    pWindowImpl->mbOverlapWin = bOrigOverlapWin;
}
 
void StatusBar::SetItemText( sal_uInt16 nItemId, const OUString& rText, int nCharsWidth )
{
    sal_uInt16 nPos = GetItemPos( nItemId );
 
    if ( nPos == STATUSBAR_ITEM_NOTFOUND )
        return;
 
    ImplStatusItem* pItem = mvItemList[ nPos ].get();
 
    if ( pItem->maText == rText )
        return;
 
    pItem->maText = rText;
 
    // adjust item width - see also DataChanged()
    tools::Long nFudge = GetTextHeight()/4;
 
    tools::Long nWidth;
    if (nCharsWidth != -1)
    {
        nWidth = GetTextWidth(u"0"_ustr,0,-1,nullptr,
                    SalLayoutGlyphsCache::self()->GetLayoutGlyphs(GetOutDev(),u"0"_ustr));
        nWidth = nWidth * nCharsWidth + nFudge;
    }
    else
    {
        pItem->mLayoutGlyphsCache.reset();
        nWidth = GetTextWidth( pItem->maText,0,-1,nullptr, pItem->GetTextGlyphs(GetOutDev())) + nFudge;
    }
 
    if( (nWidth > pItem->mnWidth + STATUSBAR_OFFSET) ||
        ((nWidth < pItem->mnWidth) && (mnDX - STATUSBAR_OFFSET) < mnItemsWidth  ))
    {
        pItem->mnWidth = nWidth + STATUSBAR_OFFSET;
        ImplFormat();
        Invalidate();
    }
 
    // re-draw item if StatusBar is visible and UpdateMode active
    if ( pItem->mbVisible && !mbFormat && ImplIsItemUpdate() )
    {
        tools::Rectangle aRect = ImplGetItemRectPos(nPos);
        Invalidate(aRect);
        PaintSelfAndChildrenImmediately();
    }
}
 
const OUString& StatusBar::GetItemText( sal_uInt16 nItemId ) const
{
    sal_uInt16 nPos = GetItemPos( nItemId );
 
    assert( nPos != STATUSBAR_ITEM_NOTFOUND );
 
    return mvItemList[ nPos ]->maText;
}
 
void StatusBar::SetItemCommand( sal_uInt16 nItemId, const OUString& rCommand )
{
    sal_uInt16 nPos = GetItemPos( nItemId );
 
    if ( nPos != STATUSBAR_ITEM_NOTFOUND )
    {
        ImplStatusItem* pItem = mvItemList[ nPos ].get();
 
        if ( pItem->maCommand != rCommand )
            pItem->maCommand = rCommand;
    }
}
 
const OUString & StatusBar::GetItemCommand( sal_uInt16 nItemId )
{
    sal_uInt16 nPos = GetItemPos( nItemId );
 
    if ( nPos != STATUSBAR_ITEM_NOTFOUND )
        return mvItemList[ nPos ]->maCommand;
 
    return EMPTY_OUSTRING;
}
 
void StatusBar::SetItemData( sal_uInt16 nItemId, void* pNewData )
{
    sal_uInt16 nPos = GetItemPos( nItemId );
 
    if ( nPos == STATUSBAR_ITEM_NOTFOUND )
        return;
 
    ImplStatusItem* pItem = mvItemList[ nPos ].get();
    // invalidate cache
    pItem->mLayoutGlyphsCache.reset();
    pItem->mpUserData = pNewData;
 
    // call Draw-Item if it's a User-Item
    if ( (pItem->mnBits & StatusBarItemBits::UserDraw) && pItem->mbVisible &&
         !mbFormat && ImplIsItemUpdate() )
    {
        tools::Rectangle aRect = ImplGetItemRectPos(nPos);
        Invalidate(aRect, InvalidateFlags::NoErase);
        PaintSelfAndChildrenImmediately();
    }
}
 
void* StatusBar::GetItemData( sal_uInt16 nItemId ) const
{
    sal_uInt16 nPos = GetItemPos( nItemId );
 
    if ( nPos != STATUSBAR_ITEM_NOTFOUND )
        return mvItemList[ nPos ]->mpUserData;
 
    return nullptr;
}
 
void StatusBar::RedrawItem(sal_uInt16 nItemId)
{
    if ( mbFormat )
        return;
 
    sal_uInt16 nPos = GetItemPos(nItemId);
    if ( nPos == STATUSBAR_ITEM_NOTFOUND )
        return;
 
    ImplStatusItem* pItem = mvItemList[ nPos ].get();
    if ((pItem->mnBits & StatusBarItemBits::UserDraw) &&
        pItem->mbVisible && ImplIsItemUpdate())
    {
        tools::Rectangle aRect = ImplGetItemRectPos(nPos);
        Invalidate(aRect);
        PaintSelfAndChildrenImmediately();
    }
}
 
void StatusBar::SetHelpText( sal_uInt16 nItemId, const OUString& rText )
{
    sal_uInt16 nPos = GetItemPos( nItemId );
 
    if ( nPos != STATUSBAR_ITEM_NOTFOUND )
        mvItemList[ nPos ]->maHelpText = rText;
}
 
const OUString& StatusBar::GetHelpText( sal_uInt16 nItemId ) const
{
    sal_uInt16 nPos = GetItemPos( nItemId );
 
    assert ( nPos != STATUSBAR_ITEM_NOTFOUND );
 
    ImplStatusItem* pItem = mvItemList[ nPos ].get();
    if ( pItem->maHelpText.isEmpty() && ( !pItem->maHelpId.isEmpty() || !pItem->maCommand.isEmpty() ))
    {
        Help* pHelp = Application::GetHelp();
        if ( pHelp )
        {
            if ( !pItem->maCommand.isEmpty() )
                pItem->maHelpText = pHelp->GetHelpText( pItem->maCommand );
            if ( pItem->maHelpText.isEmpty() && !pItem->maHelpId.isEmpty() )
                pItem->maHelpText = pHelp->GetHelpText( pItem->maHelpId );
        }
    }
 
    return pItem->maHelpText;
}
 
void StatusBar::SetQuickHelpText( sal_uInt16 nItemId, const OUString& rText )
{
    sal_uInt16 nPos = GetItemPos( nItemId );
 
    if ( nPos != STATUSBAR_ITEM_NOTFOUND )
        mvItemList[ nPos ]->maQuickHelpText = rText;
}
 
const OUString& StatusBar::GetQuickHelpText( sal_uInt16 nItemId ) const
{
    sal_uInt16 nPos = GetItemPos( nItemId );
 
    assert ( nPos != STATUSBAR_ITEM_NOTFOUND );
 
    ImplStatusItem* pItem = mvItemList[ nPos ].get();
    return pItem->maQuickHelpText;
}
 
void StatusBar::SetHelpId( sal_uInt16 nItemId, const OUString& rHelpId )
{
    sal_uInt16 nPos = GetItemPos( nItemId );
 
    if ( nPos != STATUSBAR_ITEM_NOTFOUND )
        mvItemList[ nPos ]->maHelpId = rHelpId;
}
 
void StatusBar::StartProgressMode( const OUString& rText )
{
    SAL_WARN_IF( mbProgressMode, "vcl", "StatusBar::StartProgressMode(): progress mode is active" );
 
    mbProgressMode  = true;
    mnPercent       = 0;
    maPrgsTxt       = rText;
 
    // compute size
    ImplCalcProgressRect();
 
    // trigger Paint, which draws text and frame
    if ( IsReallyVisible() )
    {
        Invalidate();
        PaintSelfAndChildrenImmediately();
    }
}
 
void StatusBar::SetProgressValue( sal_uInt16 nNewPercent )
{
    SAL_WARN_IF( !mbProgressMode, "vcl", "StatusBar::SetProgressValue(): no progress mode" );
    SAL_WARN_IF( nNewPercent > 100, "vcl", "StatusBar::SetProgressValue(): nPercent > 100" );
 
    bool bInvalidate = mbProgressMode && IsReallyVisible() && (!mnPercent || (mnPercent != nNewPercent));
 
    mnPercent = nNewPercent;
 
    if (bInvalidate)
    {
        // Rate limit how often we paint, otherwise in some loading scenarios we can spend significant
        // time just painting progress bars.
        sal_uInt32 nTime_ms = osl_getGlobalTimer();
        if ((nTime_ms - mnLastProgressPaint_ms) > 100)
        {
            Invalidate(maPrgsFrameRect);
            PaintSelfAndChildrenImmediately();
            mnLastProgressPaint_ms = nTime_ms;
        }
    }
}
 
void StatusBar::EndProgressMode()
{
    SAL_WARN_IF( !mbProgressMode, "vcl", "StatusBar::EndProgressMode(): no progress mode" );
 
    mbProgressMode = false;
    maPrgsTxt.clear();
 
    if ( IsReallyVisible() )
    {
        Invalidate();
        PaintSelfAndChildrenImmediately();
    }
}
 
void StatusBar::SetText(const OUString& rText)
{
    if ((GetStyle() & WB_RIGHT) && !mbProgressMode && IsReallyVisible() && IsUpdateMode())
    {
        if (mbFormat)
        {
            Invalidate();
            Window::SetText(rText);
        }
        else
        {
            Invalidate();
            Window::SetText(rText);
            PaintSelfAndChildrenImmediately();
        }
    }
    else if (mbProgressMode)
    {
        maPrgsTxt = rText;
        if (IsReallyVisible())
        {
            Invalidate();
            PaintSelfAndChildrenImmediately();
        }
    }
    else
    {
        Window::SetText(rText);
    }
}
 
Size StatusBar::CalcWindowSizePixel() const
{
    size_t  i = 0;
    size_t  nCount = mvItemList.size();
    tools::Long    nOffset = 0;
    tools::Long    nCalcWidth = STATUSBAR_OFFSET_X*2;
    tools::Long    nCalcHeight;
 
    while ( i < nCount )
    {
        ImplStatusItem* pItem = mvItemList[ i ].get();
        nCalcWidth += pItem->mnWidth + nOffset;
        nOffset = pItem->mnOffset;
        i++;
    }
 
    tools::Long nMinHeight = std::max( static_cast<int>(GetTextHeight()), STATUSBAR_MIN_HEIGHT);
    const tools::Long nBarTextOffset = STATUSBAR_OFFSET_TEXTY*2;
    tools::Long nProgressHeight = nMinHeight + nBarTextOffset;
 
    if( IsNativeControlSupported( ControlType::Progress, ControlPart::Entire ) )
    {
        ImplControlValue aValue;
        tools::Rectangle aControlRegion( Point(), Size( nCalcWidth, nMinHeight ) );
        tools::Rectangle aNativeControlRegion, aNativeContentRegion;
        if( GetNativeControlRegion( ControlType::Progress, ControlPart::Entire,
                    aControlRegion, ControlState::ENABLED, aValue,
                    aNativeControlRegion, aNativeContentRegion ) )
        {
            nProgressHeight = aNativeControlRegion.GetHeight();
        }
    }
 
    nCalcHeight = nMinHeight+nBarTextOffset;
    if( nCalcHeight < nProgressHeight+2 )
        nCalcHeight = nProgressHeight+2;
 
    return Size( nCalcWidth, nCalcHeight );
}
 
void StatusBar::SetAccessibleName( sal_uInt16 nItemId, const OUString& rName )
{
    sal_uInt16 nPos = GetItemPos( nItemId );
 
    if ( nPos != STATUSBAR_ITEM_NOTFOUND )
    {
        ImplStatusItem* pItem = mvItemList[ nPos ].get();
 
        if ( pItem->maAccessibleName != rName )
        {
            pItem->maAccessibleName = rName;
            CallEventListeners( VclEventId::StatusbarNameChanged, reinterpret_cast<void*>(pItem->mnId) );
        }
    }
}
 
const OUString& StatusBar::GetAccessibleName( sal_uInt16 nItemId ) const
{
    sal_uInt16 nPos = GetItemPos( nItemId );
 
    assert ( nPos != STATUSBAR_ITEM_NOTFOUND );
 
    return mvItemList[ nPos ]->maAccessibleName;
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V530 The return value of function 'DrawFrame' is required to be utilized.

V1053 Calling the 'ApplySettings' virtual function indirectly in the constructor may lead to unexpected result at runtime. Check lines: 'status.cxx:151', 'status.cxx:142', 'status.cxx:212', 'status.hxx:107'.

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